diff --git a/src-docs/SUMMARY.md b/src-docs/SUMMARY.md index 76ed3b5..1e838a9 100644 --- a/src-docs/SUMMARY.md +++ b/src-docs/SUMMARY.md @@ -12,6 +12,8 @@ - [selfAddr & selfName](./spec/selfAddr_and_selfName.md) - [joinRealtimeChannel](./spec/joinRealtimeChannel.md) - [Messenger implementations](./spec/messenger.md) + - [CHANGELOG](./spec/CHANGELOG.md) + - [Shared Web Application state](./shared_state/README.md) - [Detecting conflicts](./shared_state/conflicts.md) - [Theory of Conflict-free Replicated Data Types (CRDTs)](./shared_state/crdts.md) @@ -23,3 +25,4 @@ - [Packaging](./faq/packaging.md) - [Compatibility](./faq/compat.md) - [Storage](./faq/storage.md) + diff --git a/src-docs/faq/compat.md b/src-docs/faq/compat.md index 7d0cb8c..5c9f4c8 100644 --- a/src-docs/faq/compat.md +++ b/src-docs/faq/compat.md @@ -1,19 +1,6 @@ # Compatibility -## Other APIs and Tags Usage Hints - -Webxdc apps run in a restricted environment, but the following practices are permitted: - -- `localStorage`, `sessionStorage`, `indexedDB` -- `visibilitychange` events -- `window.navigator.language` -- internal links, such as `` -- `mailto` links, such as `` -- `` is useful especially as webviews from different platforms have different defaults -- `` allows importing of files for further - processing; see [`sendToChat()`](../spec/sendToChat.md) for a way to export files - ### Discouraged Practises - `document.cookie` is known not to work on desktop and iOS—use `localStorage` instead diff --git a/src-docs/spec/CHANGELOG.md b/src-docs/spec/CHANGELOG.md new file mode 100644 index 0000000..2f2dda4 --- /dev/null +++ b/src-docs/spec/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## [1.2] - 2024-12-15 + +### New APIs + +- modify `webxdc.selfAddr` to return a per-app identifier + that can be used for the new "notify" list + +- add new `update.notify` attribute to allow causing + a user-visible system notification to raise awareness + that something in an app changed + +- add new `update.href` attribute so that clicking info messages + will provide a "deep link" into an app to ease navigation + +- add new `webxdc.sendUpdateMaxInterval` and `webxdc.sendUpdateMaxSize` + rate limits which helps apps to adapt to transport characteristics/limitations + + +### Deprecations + +- `webxdc.sendUpdate` does not take a "descr" argument anymore. + + +### Other changes + +- shifted "supported" DOM/Javascript features from the FAQ + into the messenger spec section + +- improved wording in several places diff --git a/src-docs/spec/format.md b/src-docs/spec/format.md index 63a6df2..361b5d3 100644 --- a/src-docs/spec/format.md +++ b/src-docs/spec/format.md @@ -2,18 +2,20 @@ ## Webxdc Container File Format - a Webxdc app is a **ZIP-file** with the extension `.xdc` + - the ZIP-file MUST use the default compression methods as of RFC 1950, this is "Deflate" or "Store" + - the ZIP-file MUST contain at least the file `index.html` + - the ZIP-file MAY contain a `manifest.toml` and `icon.png` or `icon.jpg` files -- if the webxdc app is started, `index.html` MUST be opened in a [restricted webview](./messenger.md#webview-constraints-for-running-apps) that only allows accessing - resources from the ZIP-file. + ### The manifest.toml File If the ZIP-file contains a `manifest.toml` in its root directory, -the following basic information MUST be read from it: +the following basic information MUST be read from it: ```toml name = "My App Name" @@ -27,7 +29,7 @@ source_code_url = "https://example.org/orga/repo" Messenger implementors may make the url accessible via a "Help" menu in the webxdc window. -### Icon Files +### Icon Files If the ZIP-root contains an `icon.png` or `icon.jpg`, these files are used as the icon for the webxdc. diff --git a/src-docs/spec/joinRealtimeChannel.md b/src-docs/spec/joinRealtimeChannel.md index ea3558a..fc38b51 100644 --- a/src-docs/spec/joinRealtimeChannel.md +++ b/src-docs/spec/joinRealtimeChannel.md @@ -34,6 +34,8 @@ Calling `setListener` a second time will replace the previous listener. ## `realtimeChannel.send(data)` Send a `Uint8Array` data item to connected peers. +The size of the array must not exceed 128000 bytes. + There is no guarantee anyone is receiving sent data because there might be no currently listening peers, or network connections fail. diff --git a/src-docs/spec/messenger.md b/src-docs/spec/messenger.md index 4da4bd3..6fae320 100644 --- a/src-docs/spec/messenger.md +++ b/src-docs/spec/messenger.md @@ -9,49 +9,69 @@ The same webxdc container file shared in two separate chat messages will be regarded as two separate "webxdc apps" and the two apps can not communicate with, or even know about, each other. -### Webview Constraints for Running Apps +### Webview Constraints -When starting a web view for a webxdc app to run, messenger implementors: +When starting a web view for a webxdc app to run, messenger implementors -- MUST run the [webxdc container file](./format.md) in a constrained, - network-isolated webview that - MUST deny all forms of internet access. - If you don't do this - unsuspecting users may leak data of their private interactions to outside third parties. - You do not need to offer "privacy" or "cookie" consent screens as - there is no way a webxdc app can implicitly transfer user data to the internet. +- MUST serve all resources from the [webxdc container file](./format.md) + to the webxdc application. + +- on initial opening, MUST open root URL for the webview with `index.html` appended +- MUST implement the [Webxdc Javascript API](api.md) + and serve a `webxdc.js` file accordingly. + +- MUST deny all forms of Internet access; + if you don't restrict internet access + unsuspecting users may run 3rd party apps + that leak data of private interactions to outside third parties. + See ["Bringing E2E privacy to the web"](https://delta.chat/en/2023-05-22-webxdc-security) + which contains a deep discussion of the unique privacy guarantees of webxdc. + +- MUST support `localStorage`, `sessionStorage` and `indexedDB` + +- MUST isolate all storage and state of one webxdc app from any other + +- MUST support `visibilitychange` events -- MUST allow unrestricted use of DOM storage (local storage, indexed db and co), - but make sure it is scoped to each webxdc app so they can not delete or modify - the data of other webxdc content. +- MUST support `window.navigator.language` -- MUST inject `webxdc.js` and implement the - [Webxdc Javascript API](api.md) so that messages are relayed and shown in chats. +- MUST support `window.location.href` but you can not specify or assume anything + about the protocol scheme or domain part of the url. -- MUST make sure the standard JavaScript API works as described at - [Other APIs and Tags Usage Hints](../faq/compat.md#other-apis-and-tags-usage-hints). +- MUST support HTML links such as `` + +- MUST support `mailto` links, such as `` + +- MUST support `` is useful especially as webviews from different platforms have different defaults + +- MUST support `` to allow importing of files; + see [`sendToChat()`](../spec/sendToChat.md) for a way to export files. -In ["Bringing E2E privacy to the web"](https://delta.chat/en/2023-05-22-webxdc-security) -Delta Chat developers discuss the unique privacy guarantees of webxdc, -and which mitigations messengers using Chromium webviews need to implement to satisfy them. ### UI Interactions in Chats - Text from `update.info` SHOULD be shown in the chats - and tapping them should jump to their webxdc message + and tapping them SHOULD open the webxdc app directly. + If `update.href` was set then the webxdc app MUST + be started with the root URL for the webview with + the value of `update.href` appended. -- The most recent text from `update.document` and `update.summary` SHOULD be shown inside the webxdc message, +- The most recent text from `update.document` + and `update.summary` SHOULD be shown inside the webxdc message, together with name and icon. Only one line of text SHOULD be shown and truncation is fine - as webxdc devs SHOULD NOT be encouraged to send long texts here. + as webxdc application developers SHOULD NOT be encouraged to send long texts here. + +- A "Start" button MUST be offered that runs the webxdc app. + Note that there is no need to ask for "privacy" or "cookie" consent because + there is no way a webxdc app can implicitly transfer user data to the internet. -- A "Start" button SHOULD run the webxdc app. ### Example Messenger Implementations -- [Android using Java](https://github.com/deltachat/deltachat-android/blob/master/src/org/thoughtcrime/securesms/WebxdcActivity.java) +- [Delta Chat Android using Java](https://github.com/deltachat/deltachat-android/blob/master/src/org/thoughtcrime/securesms/WebxdcActivity.java) -- [iOS using Swift](https://github.com/deltachat/deltachat-ios/blob/master/deltachat-ios/Controller/WebxdcViewController.swift) +- [Delta Chat iOS using Swift](https://github.com/deltachat/deltachat-ios/blob/master/deltachat-ios/Controller/WebxdcViewController.swift) -- [Desktop using TypeScript](https://github.com/deltachat/deltachat-desktop/blob/786b7514d69ffb723bbe6e706494852a2641bfcd/src/main/deltachat/webxdc.ts) +- [Delta Chat Desktop using TypeScript](https://github.com/deltachat/deltachat-desktop/blob/786b7514d69ffb723bbe6e706494852a2641bfcd/src/main/deltachat/webxdc.ts) diff --git a/src-docs/spec/selfAddr_and_selfName.md b/src-docs/spec/selfAddr_and_selfName.md index 84874d4..d71684d 100644 --- a/src-docs/spec/selfAddr_and_selfName.md +++ b/src-docs/spec/selfAddr_and_selfName.md @@ -1,27 +1,77 @@ # selfAddr & selfName +## selfName + +```js +window.webxdc.selfName +``` + +`selfName` is the nick or display name for the user +which the webxdc application may show in its user interface. + + ## selfAddr ```js window.webxdc.selfAddr ``` -A string with an unique ID identifying the user in the current webxdc. -Every user of an webxdc must get a different ID -and that ID must be the same if the webxdc is started again later for the same user. -The same user in different webxdc, however, may have different IDs. +A string ("unique ID") identifying the user in the current webxdc application. +Every user of a webxdc application must get a different `selfAddr`. +The `selfAddr` must be the same if the webxdc is started again later for the same user, +on the same or on a different device. +The same user opening a different webxdc application, however, +SHOULD have a different `selfAddr` to avoid linkability between apps: +even if a web app manipulates users to share `selfAddr` values via copy+paste +to another web app, addresses between the two web apps should not be linkable. -Especially useful if you want to differentiate between different peers - -just send the ID along with the payload, -and, if needed, compare the payload addresses against `selfAddr` later on. +Note that `selfAddr` + +- has no meaning outside the webxdc application, + +- should not be shown in the user interface of the webxdc application + (use `selfName` instead). -## selfName + + +## Example using selfAddr and selfName + +Here is a simple chat app that sends out a reply using the display names +but uses the addresses for notifications. ```js -window.webxdc.selfName -``` -Name of the current account, as defined in settings. -If empty, this defaults to the peer's address. +// Receive a message from anyone in the chat +let users = new Set(); + +setUpdateListener((update) => { + const prompt = `${update.payload.senderName} (${update.payload.senderAddr}):`; + users.add(update.payload.senderAddr); + console.log(`${prompt} ${update.message}`); +}) + +// start some user interface which calls the following function for +// message sending + +sendMessage(text) => { + let payload = { + senderAddr: webxdc.selfAddr, + senderName: webxdc.selfName, + message: text + }; + + // notify all users who ever sent a message in the chat app + let notify = {}; + for (const addr of users) { + notify[addr] = `new message from ${webxdc.selfName}`; + } + + sendUpdate({ + payload: payload, + notify: notify + }) +}) +``` +[`sendUpdate()`]: ./sendUpdate.html diff --git a/src-docs/spec/sendUpdate.md b/src-docs/spec/sendUpdate.md index 147ed5b..d7fedeb 100644 --- a/src-docs/spec/sendUpdate.md +++ b/src-docs/spec/sendUpdate.md @@ -6,36 +6,96 @@ window.webxdc.sendUpdate(update, descr); Send an update to all peers. -- `update`: an object with the following properties: - - `update.payload`: string, number, boolean, array, object or `null`. - MUST NOT be `undefined`. - Everything that is not JSON serializable will be skipped, - this especially affects Binary data buffers as used in `File`, `Blob`, `Int*Array` etc.; - if needed, use eg. base64. - - `update.info`: optional, short, informational message that will be added to the chat, - e.g. "Alice voted" or "Bob scored 123 in MyGame". - Do not add linebreaks; implementations will truncate the text at about 50 characters or less. - If there are series of info messages, older ones may be dropped. - use this option sparingly to not spam the chat. - - `update.document`: optional, name of the document in edit - (eg. the title of a poll or the name of a text in an editor) - Implementations show the document name e.g. beside the app icon or in the title bar. - MUST NOT be used if the webxdc does not create documents, e.g. in games. - Do not add linebreaks; implementations will truncate the text at about 20 characters or less. - - `update.summary`: optional, short text, shown beside the app icon; - it is recommended to use some aggregated value, e.g. "8 votes", "Highscore: 123". - Do not add linebreaks; implementations will truncate the text at about 20 characters or less. - -- `descr`: short, human-readable description what this update is about. - this is shown e.g. as a fallback text in an e-mail program. - All peers, including the sending one, will receive the update by the callback given to [`setUpdateListener()`](./setUpdateListener.html). There are situations where the user cannot send messages to a chat, e.g. if the webxdc instance comes as a contact request or if the user has left a group. -In these cases, you can still call `sendUpdate()`, -however, the update won't be sent to other peers -and you won't get the update by [`setUpdateListener()`](./setUpdateListener.html). +In these cases, an app can still call `sendUpdate()`, +but the update won't be sent to other peers +and it will not be passed to [`setUpdateListener()`](./setUpdateListener.html). + +The `update` object has the following properties: + +- `update.payload`: string, number, boolean, array, object or `null`. + MUST NOT be `undefined`. + Everything that is not JSON serializable will be skipped, + this especially affects Binary data buffers as used in `File`, `Blob`, `Int*Array` etc.; + if needed, use eg. base64. + +- `update.info`: optional, short, informational message that will be added to the chat, + e.g. "Alice voted" or "Bob scored 123 in MyGame". + Do not add linebreaks; implementations will truncate the text at about 50 characters or less. + If there are series of info messages, older ones may be dropped. + use this option sparingly to not spam the chat. + +- `update.href`: optional string that specifies a relative URL + (for example `index.html#about`) + When a receiver starts the webxdc app based on the update object + the app will be navigated to the `href` location with the application root url prepended. + +- `update.document`: optional, name of the document in edit + (eg. the title of a poll or the name of a text in an editor). + Implementations show the document name e.g. beside the app icon or in the title bar. + MUST NOT be used if the webxdc does not create documents, e.g. in games. + Do not add linebreaks; implementations will truncate the text at about 20 characters or less. + +- `update.summary`: optional, short text, shown beside the app icon; + it is recommended to use some aggregated value, e.g. "8 votes", "Highscore: 123". + Do not add linebreaks; implementations will truncate the text + at about 20 characters or less. + +- `update.notify`: optional dictionary mapping an [address](./selfAddr_and_selfName.md) + to a text that should be shown as a user-visible notification to the addressed user. + The optional special key "\*" serves as a catch-all address + whose text shall be notified if `selfAddr` is not contained in the dictionary. + + +The `descr` argument is deprecated but for backward compatibility +apps should pass an empty string. + + +## Using `info` and `href` to provide navigation + +If a user clicks on an `info` message that has a `href` specified +then the webxdc app will be started and navigate to the specified location. + +For example, a calendar app can send an update to add a new calendar event. +The info would be shown in the chat and if the user taps or clicks on it +the app is started (if it is not running already) and navigates +to the new href which can for example directly show the day and highlight the added event. + + +## Messaging layer limits for sendUpdate + +A messaging layer SHOULD expose the following limits to web applications: + +- `webxdc.sendUpdateInterval` indicates the number of milliseconds + to wait for before calling `sendUpdate()` again since the last call. + If the webxdc app calls `sendUpdate` earlier than the specified interval + the messaging layer may delay updates for much longer + than the interval. + +- `webxdc.sendUpdateMaxSize` is the maximum number of bytes that + the messaging layer will accept for a serialized `update` object + passed into a `sendUpdate` invocation. + +If the messaging layer does not expose these limits +then webxdc apps should assume the following defaults: + +- `sendUpdateInterval = 10000` + +- `sendUpdateMaxSize = 128000` + +### Examples for using sendUpdate limits +If using the default limits, +a webxdc editor app could send combined changes in a single `sendUpdate` call +at most every 10 seconds +so that the messaging layer will attempt to send each update immediately. +If the editor app were to call `sendUpdate` every 2 seconds instead, +updates might get queued for a longer time than just the `sendUpdateInterval`. +Moreover, an editor can also inspect `sendUpdateMaxSize` +and send oversized updates in smaller chunks +and recombine them on the receiving side. diff --git a/src-docs/spec/setUpdateListener.md b/src-docs/spec/setUpdateListener.md index ff5a063..b9ad48c 100644 --- a/src-docs/spec/setUpdateListener.md +++ b/src-docs/spec/setUpdateListener.md @@ -6,10 +6,13 @@ let promise = window.webxdc.setUpdateListener((update) => {}, serial); With `setUpdateListener()` you define a callback that receives the updates sent by [`sendUpdate()`]. The callback is called for updates sent by you or other peers. -The `serial` specifies the last serial that you know about (defaults to 0). -The returned promise resolves when the listener has processed all the update messages known at the time when `setUpdateListener` was called. +The `serial` specifies the last serial that you know about (defaults to 0). +The returned promise resolves when the listener has processed all the update messages known at the time when `setUpdateListener` was called. -Each `update` which is passed to the callback comes with the following properties: + +## Application update object + +Each `update` which is passed to the callback comes with the following properties: - `update.payload`: equals the payload given to [`sendUpdate()`] @@ -23,12 +26,18 @@ Each `update` which is passed to the callback comes with the following propertie - `update.info`: optional, short, informational message (see [`sendUpdate()`]) +- `update.href`: in-app "deep link" reference + that can be optionally provided by the update sender via [`SendUpdate()`]. + - `update.document`: optional, document name as set by the sender, (see [`sendUpdate()`]). Implementations show the document name e.g. beside the app icon or in the title bar. There is no need for the receiver to further process this information. - `update.summary`: optional, short text, shown beside icon (see [`sendUpdate()`]) +- `update.notify`: optional, list of user addresses + intended to be notified by the sender + Calling `setUpdateListener()` multiple times is undefined behavior: in current implementations only the last invocation works. [`sendUpdate()`]: ./sendUpdate.html diff --git a/src-docs/webxdc.d.ts b/src-docs/webxdc.d.ts index 076a6af..7338a49 100644 --- a/src-docs/webxdc.d.ts +++ b/src-docs/webxdc.d.ts @@ -89,9 +89,8 @@ interface Webxdc { /** * Webxdc are usually shared in a chat and run independently on each peer. To get a shared status, the peers use sendUpdate() to send updates to each other. * @param update status update to send - * @param description short, human-readable description what this update is about. this is shown eg. as a fallback text in an email program. */ - sendUpdate(update: SendingStatusUpdate, description: string): void; + sendUpdate(update: SendingStatusUpdate): void; /** * Send a message with file, text or both to a chat. * Asks user to what Chat to send the message to.