18 KiB
MSC3489 - m.beacon: Sharing streams of location data with history
Problem
A common communication feature is sharing live location data with other users. Use cases include helping users find each other and asset tracking. Matrix doesn't currently provide a way of doing this.
Therefore, we'd like to provide a way for users to efficiently advertise the time-limited real-time location of themselves or arbitrary assets to other users in a room - while by default preserving the history of the location data in the room by storing it as relational timeline events.
The rationale for persisting location data is to support the use cases of:
- Publishing things like Strava-style exercise data
- Bridging to things like ADS-B sources for sharing plane flight path data.
- Logistics use cases where you want to track where your fleet of vehicles/ships has been, or the packages within them
- Supporting scenarios where you're trying to rendezvous with someone/something which will be aided by seeing their historical whereabouts (e.g. search-and-rescue operations; enhanced Find-my-iPhone style use cases).
For purely ephemeral location sharing, see MSC3672.
Proposal
This MSC adds the ability to publish real-time location beacons to Matrix by
building on
MSC3488,
which allows sharing of single static locations via the m.location
event
type.
We introduce two types of event to describe beacons:
-
m.beacon_info
: a single state event containing metadata about this beacon (e.g. how long it will remain live). -
m.beacon
: multiple message events containing the actual locations at various times. All of them.beacon
events refer to the originalm.beacon_info
.
m.beacon_info
- "I am going to share live location"
An example m.beacon_info
event is:
{
"type": "m.beacon_info",
"state_key": "@matthew:matrix.org",
"content": {
"description": "The Matthew Tracker",
"live": true,
"m.ts": 1436829458432,
"timeout": 86400000,
"m.asset": { "type": "m.self" }
}
}
description
is the same as an m.location
description
(MSC3488).
live
should be true when a user starts sharing location.
m.ts
is the time when location sharing started, in milliseconds since the
epoch.
timeout
is the length of time in milliseconds that the location will be live.
So the location will stop being shared at m.ts
+ timeout
milliseconds since
the epoch.
m.asset
identifies the type of asset being tracked as per
MSC3488.
state_key
of m.beacon_info
The state_key
of the m.beacon_info
identifies the beacon. It must start
with the mxid of the sender, and if it contains more characters, the first
character after the mxid must be underscore, to match
MSC3757 and
MSC3779.
If state_key
exactly equals the sender's mxid, then the current Matrix spec
prevents anyone else from overwriting the state from this event. Until
state_key
MSC3757 (or
some equivalent mechanism) lands, state_key
will not be allowed to contain
further characters. After it lands, adding underscore and further characters
will allow one user to create multiple beacons.
Obviously, if the state_key
always equals the sender's mxid, each user can
only share a single beacon in each room at any time. We recommend that clients
work in this mode until
MSC3757 is
available.
To allow multiple beacons per user (e.g. one per device), we use a state_key
of mxid plus underscore, and an identifier. For example:
{
"type": "m.beacon_info",
"state_key": "@matthew:matrix.org_46583241", // inspired by MSC3757
"content": {
"description": "Matthew's other phone",
"live": true,
"m.ts": 1436829458432,
"timeout": 86400000,
"m.asset": { "type": "m.self" }
}
}
This style of state_key
will not work (is not allowed) until
MSC3757 is
available.
m.beacon
- "I am here"
The actual location data is sent in m.beacon
events, which have a reference
relationship to the original m.beacon_info
event.
m.beacon
events should be sent at a variable rate which is meaningful for
the use case for the asset being tracked. This rate should not be more
frequent than sending an event every 2 seconds. If the asset is not moving,
the location should still be refreshed on a regular basis to convey that
information - e.g. every 30 seconds.
An example m.beacon
event is:
{
"type": "m.beacon",
"sender": "@matthew:matrix.org",
"content": {
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$beacon_info_event_id"
},
"m.location": {
"uri": "geo:51.5008,0.1247;u=35",
"description": "Arbitrary beacon information"
},
"m.ts": 1636829458432,
}
}
m.relates_to
is a reference relationship
(MSC3267).
The event_id
is the ID of the m.beacon_info
event emitted when the user
started this live share.
m.location
is identical to the m.location
section of the m.location
event
type in MSC3488. As such,
it must contain a uri
field with an RFC5870 geo:
URI. It can contain an
optional description
field to provide user-facing updates e.g. the current
address of this location.
m.ts
is the time in milliseconds since the epoch when the location was
measured.
Stopping sharing
To stop sharing, the client should emit an additional m.beacon_info
event that is identical to the first, except with live: false
.
The client that initiated a share should stop sharing if:
- the timeout on a live share has expired, or
- the user requested to stop sharing, or
- the user revoked permissions on their device to share location.
For example:
{
"type": "m.beacon_info",
"state_key": "@matthew:matrix.org_46583241",
"content": {
"description": "Matthew's other phone",
"live": false, // Stop sharing
"m.ts": 1436829458432,
"timeout": 86400000,
"m.asset": { "type": "m.self" }
}
}
Deciding which shared locations are live
A location share is "live" if the sender is currently sharing their location.
Client apps will often want to determine two related but different pieces of information about the shares in a room:
- Which location shares are live
- How to display a single location share event (e.g. in a timeline)
Which locations shares are live
To determine who is currently sharing their location in a room, clients should
examine the room state for events of type m.beacon_info
.
For each m.beacon_info
event in the room state, it is live if:
live
istrue
, andm.ts + timeout < now_ts
wherenow_ts
is the current time in milliseconds since the epoch.
Otherwise it is not live.
How to display a single location share event
For clients with timelines, we expect that a map or similar will appear in the
timeline whenever a m.beacon_info
event with live: true
is sent, and
related m.beacon
events will cause that map to update. Stopping sharing
(sending a m.beacon_info
event with live: false
) might not create any new
visible item in the timeline.
For a map on a timeline associated with an initial m.beacon_info
, to
determine whether that map is "live", clients should:
- look in the room state for an
m.beacon_info
event with the samestate_key
, and - follow the rules in the previous section ("Which location shares are live").
Redacting shared locations
Because location information is private, it is vital that people are able to delete information that they no longer want to reveal, or that was inadvertently posted to the wrong room.
We expect that clients will provide a single UI element that redacts all events
for a live sharing session. This includes the original m.beacon_info
event,
all m.beacon
events related to it, and any additional m.beacon_info
event
sent to stop sharing.
Redaction should be done in the normal way, using the redact
API as specified
in section
7.9 Redactions of
the Client-Server API. (See also
MSC2244: Mass redactions.)
Permission to share location
Since the m.beacon_info
event is a state event, users who wish to share their
live location will need permission to send state events. By default, users with
power level 50 are able to send state events.
To allow all users to send m.beacon_info
events, the room's power levels
should be set to something like this:
{
"state_key": "",
"type": "m.room.power_levels",
"content": {
"events": {
"m.beacon_info": 0,
...
},
...
},
...
}
This can be done during room creation, or later by sending a new
m.room.power_levels
state event.
MSC3779 is intended to
make it easier to send this kind of state event, and the state_key
s in this
proposal are deliberately designed to take advantage of MSC3779 if it is
standardised. If MSC3779 is available, users with permission to send message
events will automatically have permission to share their live location, because
the state_key
matches the pattern required to be an "owned" state event.
Alternatives
Storing location in room state
Initially this MSC considered storing location data in a separate state event but that had multiple downsides:
- No encryption without MSC3414.
- Difficult access to historical data, timeline pagination required.
- State resolution thrashing on every location update. By storing a state event for every location datapoint, we put significant load on servers' state management implementations. Current implementations may not handle this well.
Storing the location data as references as opposed to in a state event has multiple advantages:
- Location data is easily encrypted (no dependency on MSC3414).
- Doesn't thrash state resolution by storing new location points every time they change.
- Paginated history easily accessible through the
relations
endpoint. If history is not wanted, then one can use data retention policies (e.g. exploding messages) to ensure it does not accumulate unnecessarily. - Allows other users to request data from a beacon.
Sending location data using ephemeral data units (EDUs)
This proposal covers use cases where location data needs to be preserved
in the room timeline. In cases where this is not required, better privacy
may be obtained by sending the m.beacon
events as EDUs.
See MSC3672 for this approach. Note that it depends on user-defined EDUs (MSC2477) and encrypted EDUs (MSC3673).
To-device messages
We could behave like MSC3401 and send location data via to-device messages to interested devices.
- Pros:
- Doesn't thrash state resolution by storing new location points every time they change
- Gets you easy E2EE automatically
- Cons:
- designs out historical geo-data use cases
- means you are reinventing the pubsub fan-out routing you get for free with Matrix events
- means you have to reinvent decentralised ACLs to check that the subscribing users are valid
- means new joiners to a room won't even be able to see the most recent location for someone
- MSC1763 data retention lets us control the historical data anyway.
Alternatively, we could negotiate a WebRTC data channel using MSC3401 and stream low-latency geospatial data over the participating devices in the room. This doesn't give us historical data, however, and requiring a WebRTC stack is prohibitively complicated for simple clients (e.g. basic IOT devices reporting spatial telemetry).
One state event per user
(As suggested by @deepbluev7)
To reduce the problem of state bloat (too many state events with different keys) and avoid the need for permission changes on state events, we could post a single state event per user, which contained a list of all the currently-shared location events, and the location events could be normal timeline events, which are updated using message edits (with m.replace
).
Advantages:
- Backwards compatible with static location sharing (MSC3488) - clients that did not understand live location sharing but did understand static would see the location changing over time due to the edits.
- Only one state event per user - less state bloat.
- No need for changes to the rules about who can modify state events (but still requires the ability for unprivileged users to create a state event with
state_key
equal to their mxid).
Disadvantages
- Potential race where multiple clients update the same state event at the same time, and one overwrites the other.
- Location events need to be fetched from the timeline before they can be used.
- Potential confusion - a live location is semantically different from a static location that has been edited.
No state events
We could send location data as normal E2EE messages, with no state events involved. However, clients would have to back-paginate in an unbounded fashion to confidently say what the various beacons are doing rather than simply fishing it out of room state.
Including velocity
We could include velocity data as well as displacement data here (especially as the W3C geolocation API provides it); this has been left for a future MSC in the interests of avoiding scope creep.
Security considerations
Location data is high risk for real-world abuse, especially if stored
unencrypted or persisted. The same security considerations apply as for
m.location
data in general.
Normally, locations should only be shared in encrypted rooms, meaning that they are transferred and stored end-to-end encrypted automatically.
Clients will probably want to warn users when they request to share location in an unencrypted room.
See "Redacting shared locations" above for information about deleting shared location data.
Unstable prefix
Until this MSC lands, the following unstable prefixes should be used:
m.beacon_info
->org.matrix.msc3672.beacon_info
m.beacon
->org.matrix.msc3672.beacon
(Note: these prefixes are shared with MSC3672. If this MSC lands first, MSC3672 can be updated to use stable prefixes.)
Until MSC3488 lands, the following unstable prefixes should be used:
m.location
->org.matrix.msc3488.location
m.ts
->org.matrix.msc3488.ts
m.asset
->org.matrix.msc3488.asset
Examples of the events with unstable prefixes:
{
"content": {
"description": "Kylie's live location",
"live": true,
"org.matrix.msc3488.asset": { "type": "m.self" },
"org.matrix.msc3488.ts": 651066205621,
"timeout": 3600000
},
"room_id": "!dypRwVXIkJkTAACHPd:element.io",
"sender": "@kylie:example.com",
"state_key": "@kylie:example.com",
"type": "org.matrix.msc3672.beacon_info",
"event_id": "$TlS7h0NHzBdZIccsSspF5CMpQE8YMT0stRern0nXscI"
}
{
"content": {
"m.relates_to": {
"event_id": "$TlS7h0NHzBdZIccsSspF5CMpQE8YMT0stRern0nXscI",
"rel_type": "m.reference"
},
"org.matrix.msc3488.location": {
"uri": "geo:8.95752746197222,12.494122581370175;u=10"
},
"org.matrix.msc3488.ts": 651066206460
},
"room_id": "!dypRwVXIkJkTAACHPd:element.io",
"sender": "@kylie:example.com",
"type": "org.matrix.msc3672.beacon",
"event_id": "$75FtAIdg9wRTdACcgq4yetaInKlKQKhExYAwMc-qW3Q"
}
Related MSCs
Dependencies
-
MSC3267: Reference relations. This MSC uses
rel_type: m.reference
from MSC3267, which in turn builds on MSC2674: Event Relationships (which has landed). -
MSC3488: Extending events with location data. This MSC re-uses the
m.location
object from MSC3488.
MSCs that will enhance this one
-
MSC3779: "Owned" State Events. If MSC3779 lands, normal users will be able to share their live location without special room permissions.
-
MSC3757: Restricting who can overwrite a state event. If MSC3757 lands, it will be possible for users to share multiple live locations with variable
state_keys
without fear that other users can overwrite theirm.beacon_info
.
Related
- MSC3672: Sharing ephemeral streams of location data. Equivalent of this MSC, but using ephemeral events, for greater privacy but reduced historical functionality.
Implementations
Element (Web, Android and iOS) implementations based on this MSC are underway in April 2022, and feedback from those implementations has been incorporated into updates of this document.