From cdc3b1226bae882d216d5416cb57c78761981274 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Thu, 16 Apr 2026 16:28:49 +0200 Subject: [PATCH 1/4] feat: introduce webxdc.isAppSender and webxdc.isBroadcast This allows apps to behave differently, dependeing on the sending context. --- src-docs/spec/CHANGELOG.md | 7 +++++++ src-docs/spec/messenger.md | 3 +++ src-docs/spec/selfAddr_and_selfName.md | 18 ++++++++++++++++++ src-docs/webxdc.d.ts | 4 ++++ 4 files changed, 32 insertions(+) diff --git a/src-docs/spec/CHANGELOG.md b/src-docs/spec/CHANGELOG.md index d5310c9..aeb0362 100644 --- a/src-docs/spec/CHANGELOG.md +++ b/src-docs/spec/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.4] - 2026-04-16 + +### New APIs + +- add `webxdc.isAppSender` and `webxdc.isBroadcast` to allow apps to determine + their role and if they are in a broadcast chat + ## [1.3] - 2026-02-11 ### Other changes diff --git a/src-docs/spec/messenger.md b/src-docs/spec/messenger.md index 0f2ca90..a94a6c2 100644 --- a/src-docs/spec/messenger.md +++ b/src-docs/spec/messenger.md @@ -54,6 +54,9 @@ When starting a web view for a webxdc app to run, messenger implementors - MUST support `` is useful especially as webviews from different platforms have different defaults +- MUST support `window.webxdc.isAppSender` and `window.webxdc.isBroadcast` + to allow apps to determine their role and if they are in a broadcast chat. + - MUST support `` to allow importing of files; see [`sendToChat()`](../spec/sendToChat.md) for a way to export files. diff --git a/src-docs/spec/selfAddr_and_selfName.md b/src-docs/spec/selfAddr_and_selfName.md index 6bbfffa..3e4bbc5 100644 --- a/src-docs/spec/selfAddr_and_selfName.md +++ b/src-docs/spec/selfAddr_and_selfName.md @@ -32,6 +32,24 @@ Note that `selfAddr` - should not be shown in the user interface of the webxdc application (use `selfName` instead). +## isAppSender + +```js +window.webxdc.isAppSender +``` + +`isAppSender` is a boolean value that is `true` if the local user is the one +who initially shared the webxdc application in the chat. +If it is `false`, the user is a receiver. + +## isBroadcast + +```js +window.webxdc.isBroadcast +``` + +`isBroadcast` is a boolean value that is `true` if the webxdc +application is shared in a broadcast chat context. ## Example using selfAddr and selfName diff --git a/src-docs/webxdc.d.ts b/src-docs/webxdc.d.ts index 7338a49..303f072 100644 --- a/src-docs/webxdc.d.ts +++ b/src-docs/webxdc.d.ts @@ -72,6 +72,10 @@ interface Webxdc { selfAddr: string; /** Returns the peer's own name. This is name chosen by the user in their settings, if there is nothing set, that defaults to the peer's address. */ selfName: string; + /** True if the current user is the sender of this webxdc app. */ + isAppSender: boolean; + /** True if the current user is in a broadcast chat. */ + isBroadcast: boolean; /** * set a listener for new status updates. * The "serial" specifies the last serial that you know about (defaults to 0). From 7c3d53c278d9d5baf3696d4a8066d1fc32621e71 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 17 Apr 2026 13:39:14 +0200 Subject: [PATCH 2/4] implemented https://github.com/webxdc/website/pull/136#issuecomment-4267486700 i.e. renaming and amending sendUpdate docs etc. --- src-docs/spec/CHANGELOG.md | 5 ++--- src-docs/spec/messenger.md | 4 ++-- src-docs/spec/selfAddr_and_selfName.md | 26 +++++++++++++++++--------- src-docs/spec/sendUpdate.md | 7 +++++-- src-docs/webxdc.d.ts | 9 +++++---- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src-docs/spec/CHANGELOG.md b/src-docs/spec/CHANGELOG.md index aeb0362..54c947d 100644 --- a/src-docs/spec/CHANGELOG.md +++ b/src-docs/spec/CHANGELOG.md @@ -4,9 +4,8 @@ ### New APIs -- add `webxdc.isAppSender` and `webxdc.isBroadcast` to allow apps to determine - their role and if they are in a broadcast chat - +- add `webxdc.appSenderAddr` and `webxdc.canSendUpdatesToAllPeers` + to allow apps to determine their role and their ability to send updates. ## [1.3] - 2026-02-11 ### Other changes diff --git a/src-docs/spec/messenger.md b/src-docs/spec/messenger.md index a94a6c2..efc4c18 100644 --- a/src-docs/spec/messenger.md +++ b/src-docs/spec/messenger.md @@ -54,8 +54,8 @@ When starting a web view for a webxdc app to run, messenger implementors - MUST support `` is useful especially as webviews from different platforms have different defaults -- MUST support `window.webxdc.isAppSender` and `window.webxdc.isBroadcast` - to allow apps to determine their role and if they are in a broadcast chat. +- MUST support `window.webxdc.appSenderAddr` and `window.webxdc.canSendUpdatesToAllPeers` + to allow apps to determine their role and their ability to send updates. - MUST support `` to allow importing of files; see [`sendToChat()`](../spec/sendToChat.md) for a way to export files. diff --git a/src-docs/spec/selfAddr_and_selfName.md b/src-docs/spec/selfAddr_and_selfName.md index 3e4bbc5..db8ee5a 100644 --- a/src-docs/spec/selfAddr_and_selfName.md +++ b/src-docs/spec/selfAddr_and_selfName.md @@ -32,24 +32,32 @@ Note that `selfAddr` - should not be shown in the user interface of the webxdc application (use `selfName` instead). -## isAppSender +## appSenderAddr ```js -window.webxdc.isAppSender +window.webxdc.appSenderAddr ``` -`isAppSender` is a boolean value that is `true` if the local user is the one -who initially shared the webxdc application in the chat. -If it is `false`, the user is a receiver. +`appSenderAddr` is the address of the peer who initially shared the webxdc application in the chat. +It can be compared to `webxdc.selfAddr` to determine whether the app is running +for the sender or a receiver. +This supports a common development model where a "central" app instance +(the sender's) processes all updates and distributes the resulting state +back to all peers. -## isBroadcast +## canSendUpdatesToAllPeers ```js -window.webxdc.isBroadcast +window.webxdc.canSendUpdatesToAllPeers ``` -`isBroadcast` is a boolean value that is `true` if the webxdc -application is shared in a broadcast chat context. +`canSendUpdatesToAllPeers` is a boolean that is `true` if the local user +can send [updates](./sendUpdate.md) to everyone in the chat. +If it is `false`, updates sent by the local user will only be seen by the app sender. + +On some platforms, such as "broadcast channels," it is technically impossible +for subscribers to discover or send updates to each other directly. +In those cases, only the app sender can distribute updates globally. ## Example using selfAddr and selfName diff --git a/src-docs/spec/sendUpdate.md b/src-docs/spec/sendUpdate.md index d7fedeb..82997b0 100644 --- a/src-docs/spec/sendUpdate.md +++ b/src-docs/spec/sendUpdate.md @@ -4,9 +4,12 @@ window.webxdc.sendUpdate(update, descr); ``` -Send an update to all peers. +Send an update to peers. +If `webxdc.canSendUpdatesToAllPeers` is true, the update is sent to all peers. +If `webxdc.canSendUpdatesToAllPeers` is false, the update is only sent to the app sender +(the peer with the address `webxdc.appSenderAddr`). -All peers, including the sending one, +All receiving 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, diff --git a/src-docs/webxdc.d.ts b/src-docs/webxdc.d.ts index 303f072..4c46822 100644 --- a/src-docs/webxdc.d.ts +++ b/src-docs/webxdc.d.ts @@ -72,10 +72,11 @@ interface Webxdc { selfAddr: string; /** Returns the peer's own name. This is name chosen by the user in their settings, if there is nothing set, that defaults to the peer's address. */ selfName: string; - /** True if the current user is the sender of this webxdc app. */ - isAppSender: boolean; - /** True if the current user is in a broadcast chat. */ - isBroadcast: boolean; + /** The address of the peer who initially shared this webxdc app. */ + appSenderAddr: string; + /** True if the current user can send updates to all peers. + * If false, updates sent by this peer will only be received by the app sender. */ + canSendUpdatesToAllPeers: boolean; /** * set a listener for new status updates. * The "serial" specifies the last serial that you know about (defaults to 0). From d767576129b0d26d531bc0257c146b2cb10e4a3b Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 17 Apr 2026 20:14:34 +0200 Subject: [PATCH 3/4] backward compat: rename so that "undefined" means it is like before the two flags were introduced. --- src-docs/spec/CHANGELOG.md | 2 +- src-docs/spec/messenger.md | 2 +- src-docs/spec/selfAddr_and_selfName.md | 11 ++++++----- src-docs/spec/sendUpdate.md | 5 +++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src-docs/spec/CHANGELOG.md b/src-docs/spec/CHANGELOG.md index 54c947d..d409869 100644 --- a/src-docs/spec/CHANGELOG.md +++ b/src-docs/spec/CHANGELOG.md @@ -4,7 +4,7 @@ ### New APIs -- add `webxdc.appSenderAddr` and `webxdc.canSendUpdatesToAllPeers` +- add `webxdc.appSenderAddr` and `webxdc.canOnlySendUpdatesToAppSender` to allow apps to determine their role and their ability to send updates. ## [1.3] - 2026-02-11 diff --git a/src-docs/spec/messenger.md b/src-docs/spec/messenger.md index efc4c18..c3bd9a5 100644 --- a/src-docs/spec/messenger.md +++ b/src-docs/spec/messenger.md @@ -54,7 +54,7 @@ When starting a web view for a webxdc app to run, messenger implementors - MUST support `` is useful especially as webviews from different platforms have different defaults -- MUST support `window.webxdc.appSenderAddr` and `window.webxdc.canSendUpdatesToAllPeers` +- MUST support `window.webxdc.appSenderAddr` and `window.webxdc.canOnlySendUpdatesToAppSender` to allow apps to determine their role and their ability to send updates. - MUST support `` to allow importing of files; diff --git a/src-docs/spec/selfAddr_and_selfName.md b/src-docs/spec/selfAddr_and_selfName.md index db8ee5a..a29ccd1 100644 --- a/src-docs/spec/selfAddr_and_selfName.md +++ b/src-docs/spec/selfAddr_and_selfName.md @@ -45,21 +45,22 @@ This supports a common development model where a "central" app instance (the sender's) processes all updates and distributes the resulting state back to all peers. -## canSendUpdatesToAllPeers +## canOnlySendUpdatesToAppSender ```js -window.webxdc.canSendUpdatesToAllPeers +window.webxdc.canOnlySendUpdatesToAppSender ``` -`canSendUpdatesToAllPeers` is a boolean that is `true` if the local user -can send [updates](./sendUpdate.md) to everyone in the chat. -If it is `false`, updates sent by the local user will only be seen by the app sender. +`canOnlySendUpdatesToAppSender` is a boolean that is `true` if updates sent +by the local user will only be seen by the app sender. +If it is `false` or `undefined`, the local user can send [updates](./sendUpdate.md) to everyone in the chat. On some platforms, such as "broadcast channels," it is technically impossible for subscribers to discover or send updates to each other directly. In those cases, only the app sender can distribute updates globally. + ## Example using selfAddr and selfName Here is a simple chat app that sends out a reply using the display names diff --git a/src-docs/spec/sendUpdate.md b/src-docs/spec/sendUpdate.md index 82997b0..f95edb2 100644 --- a/src-docs/spec/sendUpdate.md +++ b/src-docs/spec/sendUpdate.md @@ -5,10 +5,11 @@ window.webxdc.sendUpdate(update, descr); ``` Send an update to peers. -If `webxdc.canSendUpdatesToAllPeers` is true, the update is sent to all peers. -If `webxdc.canSendUpdatesToAllPeers` is false, the update is only sent to the app sender +If `webxdc.canOnlySendUpdatesToAppSender` is false, the update is sent to all peers. +If `webxdc.canOnlySendUpdatesToAppSender` is true, the update is only sent to the app sender (the peer with the address `webxdc.appSenderAddr`). + All receiving peers, including the sending one, will receive the update by the callback given to [`setUpdateListener()`](./setUpdateListener.html). From fdef7d164cfce2a7de060cd5543b4952d3ead42a Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 17 Apr 2026 21:16:50 +0200 Subject: [PATCH 4/4] mention the "appSender" flags in the shared state chapter --- src-docs/shared_state/conflicts.md | 7 +++++++ src-docs/shared_state/crdts.md | 4 +++- src-docs/shared_state/practical.md | 20 ++++++++++++++++++++ src-docs/webxdc.d.ts | 6 +++--- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src-docs/shared_state/conflicts.md b/src-docs/shared_state/conflicts.md index 0486f5f..c4ab7d6 100644 --- a/src-docs/shared_state/conflicts.md +++ b/src-docs/shared_state/conflicts.md @@ -29,6 +29,13 @@ both a Total Order of messages and a "single source of truth" can not be assumed Many webxdc applications are incapable of producing conflicts, either because they do not use the network communication APIs, or because any given data structure can only have one writer. +Alternatively, an application can designate a "central" authority +by checking [`webxdc.appSenderAddr`](../spec/selfAddr_and_selfName.html#appsenderaddr) +to identify the user who shared the app. +This peer could then authoritatively resolve conflicts or coordinate state updates. +However, developers should be aware that if the app depends entirely on the sender being online, +the app's availability will be limited when the sender is offline. + For example, many of the available [webxdc games](https://webxdc.org/apps/) offer a high-score table and how users scored will typically be reported back to the chat. Such a gaming app will simply post their user's highscore diff --git a/src-docs/shared_state/crdts.md b/src-docs/shared_state/crdts.md index cb60f8f..1ab41b7 100644 --- a/src-docs/shared_state/crdts.md +++ b/src-docs/shared_state/crdts.md @@ -146,11 +146,13 @@ This enables peers to queue updates while entirely offline, and to merge their local state with others' when they are once again able to communicate. While this behaviour can be very helpful for application developers, it may not free you entirely from having to think about network conditions. -_Eventually-consistent_ application state should generally be treated as subjective, which can be a significant shift if you are used to having a server acting as an authority. +While you can use [`webxdc.appSenderAddr`](../spec/selfAddr_and_selfName.html#appsenderaddr) to nominate a peer +(the app sender) to act as a kind of server, this introduces a dependency on that peer being online. That means that conditional behaviour that you'd usually treat as _yes_ and _no_, may instead behave more like _currently_ and _not yet_. + This section has discussed attributes of CRDTs that are mostly theoretical. The next section will give more concrete examples using **Yjs**, with a particular focus on how it can be used to accomplish common diff --git a/src-docs/shared_state/practical.md b/src-docs/shared_state/practical.md index d0c87c5..0425a25 100644 --- a/src-docs/shared_state/practical.md +++ b/src-docs/shared_state/practical.md @@ -473,6 +473,26 @@ As for the matter of duplicate ids in the `order` array, the rendering code which constructs the app's UI from this data could ignore repeated elements when iterating over them. +## Centralized coordination + +While CRDTs are designed for peer-to-peer operation, +you may sometimes want to designate one client as a "coordinator". +By comparing [`webxdc.appSenderAddr`](../spec/selfAddr_and_selfName.html#appsenderaddr) +with `webxdc.selfAddr`, +an app can identify whether it is running for the peer +that initially shared the app. +This "sender" instance can then take on special roles, +such as authoritatively resolving state conflicts, +for example by letting the app sender's updates always win. + +However, use this model with care: +if your app logic requires the sender to be online to function, +it will be unusable whenever the sender is offline or has left the chat. +One further complication occurs if the app sender +has multiple devices running the app simultaneously, +with each device possibly regarding itself as the central coordinator +and producing conflicting application states. + ## Learning more Many more examples can be found throughout diff --git a/src-docs/webxdc.d.ts b/src-docs/webxdc.d.ts index 4c46822..afc14af 100644 --- a/src-docs/webxdc.d.ts +++ b/src-docs/webxdc.d.ts @@ -74,9 +74,9 @@ interface Webxdc { selfName: string; /** The address of the peer who initially shared this webxdc app. */ appSenderAddr: string; - /** True if the current user can send updates to all peers. - * If false, updates sent by this peer will only be received by the app sender. */ - canSendUpdatesToAllPeers: boolean; + /** True if updates sent by this peer will only be received by the app sender. + * If false, the current user can send updates to all peers. */ + canOnlySendUpdatesToAppSender: boolean; /** * set a listener for new status updates. * The "serial" specifies the last serial that you know about (defaults to 0).