Recommendation: GSI CLEP (v1)
Structured RPC protocol for chat and link messages that adds region-, prim-, and script-scope targeting; channel-separated domains; and cross-border routing support
The Global Scripting Institute (GSI) is an informal organization of Second Life® users that design and test standards for efficient, flexible, and readable scripts in Second Life. "Second Life®" and "Second Life Grid™" are trademarks of Linden Research, Inc., d/b/a Linden Lab. The Global Scripting Institute and its catalog are not affiliated with or sponsored by Linden Research.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
The Chat/Link_message Encoding Protocol (CLEP) is a protocol for flexible and targeted inter-script communication in Lua and Linden Scripting Language (LSL).
Lua-style syntax is used throughout this document, but all specifications also apply to the equivalent LSL functions and event callbacks.
CLEP defines a structured JSON message format sent over chat, link message, HTTP, or email.
CLEP scripts send and receive messages on string "channels" called domains. When used via chat, to optimize channel separation, CLEP messages MUST be sent on the channel defined by passing the domain to the Channel Hash Utility Function (CHUF, see below). When used via any other method, messages from unexpected domains SHOULD be filtered out as quickly as possible for performance.
A valid CLEP message is a JSON object that contains the following key-value pairs:
| Key | Required? | Type | Value |
|---|---|---|---|
domain |
REQUIRED | string | MUST be any string signifying the CLEP domain. Listeners SHOULD drop messages that do not match a domain they are listening to. |
id |
REQUIRED | uuid | MUST be ll.GenerateKey() or some other unique UUID. |
method |
REQUIRED | {string} | MUST be an array of any strings signifying a specific operation, such as {"File", "Write"}. Methods are canonically described using dot notation, such as File.Write. |
source -> region |
RECOMMENDED | string | If included, MUST be ll.GetRegionName(). If omitted, message SHALL NOT be forwarded via cross-region relays. |
source -> prim |
RECOMMENDED | uuid | If included, MUST be ll.GetKey(). If omitted, message SHALL NOT be forwarded via any relays. |
source -> script |
RECOMMENDED | string | If included, MUST be ll.GetScriptName(). If omitted, message SHALL NOT be responded to. |
source -> email |
OPTIONAL | string | If included, MUST be the "reply-to" email address used to reach the source via ll.Email(). Intended for use only when communicating via ll.Email(). |
source -> url |
OPTIONAL | string | If included, MUST be the "HTTP-in" URL used to reach the source via ll.HTTPRequest(). Intended for use only when communicating via ll.HTTPRequest() and ll.HTTPResponse() alone is insufficient. |
target -> region |
OPTIONAL | string | If included, MUST be the name of the region in which the targeted script(s) exist. If omitted, message SHALL NOT be target-routed via cross-region relays. |
target -> prim |
OPTIONAL | uuid | If included, MUST be the uuid of the prim in which the targeted script(s) exist. If omitted, message SHALL NOT be forwarded via any relays. |
target -> root |
OPTIONAL | uuid | If included, MUST be the uuid of the root prim of the linkset in which the targeted script(s) exist. SHOULD NOT be used in conjunction with target -> prim. |
target -> link |
OPTIONAL | number | If included, MUST be the link number or LINK_* constant targeting the prim(s) of the same linkset in which the targeted script(s) exist. MUST NOT be used in conjunction with target -> prim. |
target -> script |
OPTIONAL | string | If included, MUST be the name of the targeted script(s). |
params |
OPTIONAL | any? | MAY be any valid JSON, such as a string, object, et cetera. |
result |
OPTIONAL | any? | MAY be any valid JSON. If this message is a request (not a response), this pair MUST BE omitted. |
The JSON object MAY be encoded using any of these methods:
The resulting string MAY be sent using any of these methods, where json is the encoded JSON:
link_number, 0, json, "")target_prim_uuid, CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)target_script_url, {method = "POST"}, json)httprequest_id, 200, json)target_script_address, json, "")link_number, 0, json, "")target_prim_uuid, CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)CHUF_channel, json)target_script_url, [HTTP_METHOD, "POST"], json)httprequest_id, 200, json)target_script_address, json, "")Scripters concerned about LSL support - either in their own scripts, or for compatibility with others' LSL scripts - should note the following caveats:
When CLEP messages are sent via chat, they MUST be sent on the channel generated by the CHUF for the CLEP domain used. In other words, the actual channel number MUST be derived by passing the domain string through the CHUF.
When CLEP messages are sent via any other method, channel numbers are not needed.
ll.MessageLinked(
LINK_THIS,
0,
lljson.slencode({
method = {"File", "Open"},
params = "/root/foo.bar",
domain = "My Product",
target = {}, -- since we're already only sending to LINK_THIS, we don't need target.link
source = { -- these values MUST be included, even for link messages
region = ll.GetRegionName(),
prim = ll.GetKey(),
script = ll.GetScriptName()
},
id = ll.GenerateKey() -- MUST be included in all CLEP messages; MAY be a predefined UUID (is not here)
}),
""
)
This example shows how responses SHOULD copy method, params, domain, source (as target), and id from the request:
ll.MessageLinked(
LINK_THIS,
0,
lljson.slencode({
method = method,
params = params,
domain = domain,
target = source,
source = {
region = ll.GetRegionName(),
prim = ll.GetKey(),
script = ll.GetScriptName()
},
id = id
}),
""
)
What's the difference between methods, paramses, and results? Why separate these?
method is restricted to the {string} type because it is intended to be used for message routing and script organization. Each method should refer to a specific, unique operation.
params, on the other hand, can accept any type, including nil (undefined). Data necessary to perform the operation referenced by method should be sent in params so that it can be ignored if a script receives a message with an unsupported method.
A result in a message means it is a response to a prior request. Like params, it can be any type, except that when it is nil, the message is not a response to a prior request.
Why are channels hashed? Isn't it better to have a pre-shared channel?
See the CHUF specification for more information on channel hashing.