LSL Protocol/Restrained Love Open Relay Group/key/003 draft

STATUS: draft
VERSION: 003
RECOMMENDED WITH: delay,cancel
COMMANDS:
* !x-key/<session_key>: associates key <session_key> to current session.
* !x-takeover/<session_key>: takes over control of the session with specified key.
IMPLEMENTATIONS:
* Version 002 in Dahlia's multirelay >= 0.48 and in anythingRLV >= 3.1
* Version 002 and 003 in Dahlia's multirelay >= 1.2.20 and in anythingRLV >= 3.7
=!x-key and !x-takeover=
!x-key/<session_key> associates the key <session_key> with the current session, where <session_key> is a UUID randomly generated by the controller of the current session. After a session key is set, any other controller can then take the control of this session by sending the command !x-takeover/<session_key>. By "other", it can also mean the same controller after a relog or region restart and thus a different key.
==Example==
Two controllers on the left:
* C1 is in control of the session before this exchange,
* and C2 will gain control at the end of the exchange (with all restrictions carried over under its own key).
On the right, R is the relay.
k(C1), k(C2), k(C) are the keys of the controllers and k(R) is the key of the avatar wearing the relay.
# C1 -> R: blah,k(R),!x-key/a586c562-bf27-b7db-e36e-822d0a9ba02a
# C1 <- R: blah,k(C1),!x-key/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
# C2 -> R: blah,k(R),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a
# C2 <- R: blah,k(C2),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
* At 1, C1 generates a random UUID key which must be recorded by the relay.
* Between 2 and 3, C1 gives the key to C2. Often C2 will actually be C1 after a relog and thus with a new object key, but with LSL remembering everything from before the relog, in particular the session key.
==Requirements==
General requirements:
* By supporting this x-tension, a relay associates a new key/UUID field to each RLVR session: the session key.
* The relay must acknowledge !x-key and !x-takeover using private communications only (so that nobody can eavesdrop and read the session key). Use of llRegionSayTo is therefore mandatory in case you use chat on standard RLVR channel.
Requirements concerning !x-key:
* !x-key is to be considered as a secondary behavior (it can trigger an ask dialog and open a session but the session will eventually time out if there is no primary behavior)
* On !x-key/<UUID> acceptation, the relay sets the current session key to <UUID>.
* If a relay accepts a command !x-key/<uuid> within a session which already had a key, then the new one replaces the old one.
Requirements concerning !x-takeover:
* If <UUID> is an existing session key !x-takeover/<UUID> cannot be refused by the relay (acknowledgement by "ok").
* If <UUID> is NULL_KEY or is equal to no existing session key, !x-takeover/<UUID> must be refused by the relay (acknowledgement by "ko").
* In no case !x-takeover triggers an interactive dialog or opens a session.
* On accepting !x-takeover/<UUID>
:* the relay makes the device who issued the command the controller of the session having <uuid> as session key,
:* the relay considers that if that device already was controlling a session, then that session has no controller anymore.
* The communication method (chat, email, http, ...) of the session that was taken over becomes the same as the one by which !x-takeover was received, no matter the way that session was previously accessed. For instance, if a device sends !x-takeover by chat and takes over an existing email session, then the session continues by chat.
==Recommendations==
* The controller should preferably send !x-key and !x-takeover through private channels only too. But letting session keys be "leaked" or not is only the controller's responsibility. It could actually be done on purpose.
* The key should preferably be randomly generated, so there is little risk for a session to be stolen by another device by guessing the key.
* In relay implementations, take care that the following scenario cannot happen:
:# C1 has an open session with R and no key set
:# C2 sends hijack_session,!x-takeover/00000000-0000-0000-0000-000000000000
:# Now C2 controls the session that formerly was C1's.
:For this reason, it is required that the relay refuses !x-takeover/NULL_KEY, so that the relay implementation can initialize session keys to NULL_KEY by default (which we recommend).
* Consequently, if a controller sends !x-key/00000000-0000-0000-0000-00000000000, it is equivalent to !x-key/clear (which does not formally exist); this command is mostly useless: its effect is to make any !x-takeover impossible.
* A session currently having no controller will fail all pings and thus unless !x-delay is set, a controller sending !x-takeover within a session will lose its former session (all restrictions will be released). In this case, the relay can release the current session immediately after !x-takeover is accepted.
* As a RLVR controller scriptwriter, most likely you don't want !x-takeover to be sent within an existing session, as this would mean closing/releasing it. If you do that, ask yourself if you want the session to remain in the background for some more time. If the answer is yes, don't forget to make the session resistant to !ping's by sending !x-delay/<some_more_time_in_seconds> before you send !x-takeover/<uuid> of the other session>. Also preferably set a key in the first session so it can be recovered later on.
= (DEPRECATED) !x-noping / !x-nopingclear=
:These commands are deprecated, use !x-delay and !x-delay/clear instead.
:New RLVR controllers should not use them at all. They can instead send a command like !x-delay/<some_reasonable_amount_of_seconds>/noping|!release if they need to keep the session open for <some_reasonable_amount_of_seconds>, and then !x-delay/clear/noping after !x-takeover.
:Relays can either interpret them as they used to mean, or preferably translate them to respectively to !x-delay/<some_long_enough_amount_of_time>/noping and !x-delay/clear/noping if the relay supports delay.
We remind the reader that, like with !x-noping, pings are disabled for a session as long as a delay is running within that session. The obvious advantage over !x-noping is that it forces the controller scriptwriter to think of a reasonable delay after which to release the session is meant to expire if not taken over.
For information, we recall below what !x-noping / !x-nopingclear used to mean:
==Motivation==
There usually are three ways a relay can end a session:
* when a !release is sent by the controller,
* when the user safe-words from the relay,
* and when the relay finds the controller is not present (not answering !pong to a ping after a relog or on refresh).
The command !x-noping disables the latter possibility, so that the session can persist despite the controller disappearing. This would typically happen very often if the controller is a portable HUD.
Combined with !x-key and !x-takeover, !x-ping makes it possible for that portable HUD to recover a RLVR session after its wearer relogs, possibly after a long time offline.
* !x-noping cannot exist without !x-key. Without a session key negotiated with !x-key, the relay must answer !x-noping,ko to any !x-noping. The reason of this choice is that a session with !x-noping and without session key is not cancellable anymore by a controller if the controller disappears (e.g. unrezzed).
* The command !x-key/NULL_KEY or !x-key/clear must cancel any previous !x-noping for the same reason.
==Example==
After the following sequence of commands, the user of the controller C if it is a HUD can go offline. The session will persist until the relay safe-words.
 1 C -> R: blah,k(R),!x-key/a586c562-bf27-b7db-e36e-822d0a9ba02a
 2 C <- R: blah,k(C),!x-key/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
 3 C -> R: blah,k(R),!x-noping
 4 C <- R: blah,k(C),!x-noping,ok
k'(C) is the new UUID key of C after the relog. To cancel this setting after a relog: 
 1 C -> R: blah,k(R),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a
 2 C <- R: blah,k'(C),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
 3 C -> R: blah,k(R),!x-noping/clear
 4 C <- R: blah,k'(C),!x-noping/clear,ok
To release the victim after a relog: 
 1 C -> R: blah,k(R),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a
 2 C <- R: blah,k'(C),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
 3 C -> R: blah,k(R),!release
 4 C <- R: blah,k'(C),!release,ok
To continue with the session after a relog: 
 1 C -> R: blah,k(R),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a
 2 C <- R: blah,k'(C),!x-takeover/a586c562-bf27-b7db-e36e-822d0a9ba02a,ok
 3 C -> R: blah,k(R),@xxx=add
 4 C <- R: blah,k'(C),@xxx=add,ok
==Requirements==
* !x-noping is a secondary behavior. It can trigger an interactive ask dialog, depending on settings.
* On answering "!x-noping,ok" to !x-noping, the relay commits itself not to check for presence of the controller of this session anymore and thus not to close the session because the controller fails to answer a ping.
==Caveat==
On relog, the fact there is no ping/pong exchange for a session with !x-noping does not mean the relay should not sit the avatar back to where it was sat if this session had @unsit=n as active behavior. So the key <UUID> of the sit target should still be remembered, and @sit:<UUID>=force be sent a few seconds after relog.