mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
b327bd30c3
Without this, we have hardly any enforcement. This is why the schema mistake fixed in the previous patches weren't spotted immediately. The hard work was done by: ``` $ for f in lightning-*.json; do grep -v '^ "additionalProperties": false,' $f | bagto $f; done $ for f in lightning-*.json; do sed 's/"properties": {/"additionalProperties": false, "properties": {/' $f | bagto $f; done $ make fmt-schemas ``` Then checking where 'additionalProperties: true' had been turned to false (we deliberately use it in some places where there are if statements in the schema, or occasionally where there can be arbitrary fields). [Including doc/rpc-schema-draft.json update by Shahana] Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
375 lines
15 KiB
JSON
375 lines
15 KiB
JSON
{
|
|
"$schema": "../rpc-schema-draft.json",
|
|
"type": "object",
|
|
"added": "v23.08",
|
|
"rpc": "createrune",
|
|
"title": "Command to Create/Update Rune for Authorizing Remote Peer Access",
|
|
"description": [
|
|
"The **createrune** RPC command creates a base64 string called a *rune* which can be used to access commands on this node. Each *rune* contains a unique id (a number starting at 0), and can have restrictions inside it. Nobody can remove restrictions from a rune: if you try, the rune will be rejected. There is no limit on how many runes you can issue; the node simply decodes and checks them as they are received.",
|
|
"",
|
|
"Oh, I almost forgot. Runes can also be invoked like in ancient times with the **invokerune** command. Feel the magical powers of a rune by invoking it."
|
|
],
|
|
"request": {
|
|
"required": [],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"rune": {
|
|
"type": "string",
|
|
"description": [
|
|
"If supplied, the restrictions are simple appended to that *rune* (it doesn't need to be a rune belonging to this node). If not supplied, a new *rune* is constructed, with a new unique id."
|
|
]
|
|
},
|
|
"restrictions": {
|
|
"description": [
|
|
"It can be the string `readonly`, or an array of restrictions.",
|
|
"Each restriction is an array of one or more alternatives, such as \"method is listpeers\", or \"method is listpeers OR time is before 2023\"."
|
|
],
|
|
"oneOf": [
|
|
{
|
|
"type": "array",
|
|
"description": [
|
|
"Alternatives use a simple language to examine the command which is being run:",
|
|
" * time: the current UNIX time, e.g. \"time<1656759180\".",
|
|
" * id: the node_id of the peer, e.g. \"id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605\".",
|
|
" * method: the command being run, e.g. \"method=withdraw\".",
|
|
" * per: how often the rune can be used, with suffix \"sec\" (default), \"min\", \"hour\", \"day\" or \"msec\", \"usec\" or \"nsec\". e.g. \"per=5sec\".",
|
|
" * rate: the rate limit, per minute, e.g. \"rate=60\" is equivalent to \"per=1sec\".",
|
|
" * pnum: the number of parameters. e.g. \"pnum<2\".",
|
|
" * pnameX: the parameter named X e.g. \"pnamedestination=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T\". NOTE: until v24.05, X had to remove underscores from the parameter name (e.g. `pnameamount_msat` had to be specified as `pnameamountmsat`) but that is now fixed.",
|
|
" * parrN: the N'th parameter. e.g. \"parr0=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T\".",
|
|
" * pinvX_N: parse the parameter X as an invoice (bolt11 or bolt12) and extract field N for comparison. Fails if parameter X is not present, does not parse, or N is not one of the following field names:",
|
|
" * amount",
|
|
" * description",
|
|
" * node"
|
|
],
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"type": "string",
|
|
"enum": [
|
|
"readonly"
|
|
],
|
|
"description": [
|
|
"A rune which allows most *get* and *list* commands, and the *summary* command."
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"response": {
|
|
"required": [
|
|
"rune",
|
|
"unique_id"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"rune": {
|
|
"type": "string",
|
|
"description": [
|
|
"The resulting rune."
|
|
]
|
|
},
|
|
"unique_id": {
|
|
"type": "string",
|
|
"description": [
|
|
"The id of this rune: this is set at creation and cannot be changed (even as restrictions are added)."
|
|
]
|
|
},
|
|
"warning_unrestricted_rune": {
|
|
"type": "string",
|
|
"description": [
|
|
"A warning shown when runes are created with powers that could drain your node."
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"restriction_format": [
|
|
"Restrictions are one or more alternatives. Each alternative is *name* *operator* *value*. The valid names are shown above.",
|
|
"",
|
|
"Note that if a value contains `\\`, it must be preceeded by another `\\` to form valid JSON:",
|
|
"* `=`: passes if equal ie. identical. e.g. `method=withdraw`",
|
|
"* `/`: not equals, e.g. `method/withdraw`",
|
|
"* `^`: starts with, e.g. `id^024b9a1fa8e006f1e3937f`",
|
|
"* `$`: ends with, e.g. `id$381df1cc449605`.",
|
|
"* `~`: contains, e.g. `id~006f1e3937f65f66c40`.",
|
|
"* `<`: is a decimal integer, and is less than. e.g. `time<1656759180`",
|
|
"* `>`: is a decimal integer, and is greater than. e.g. `time>1656759180`",
|
|
"* `{`: preceeds in alphabetical order (or matches but is shorter),",
|
|
" e.g. `id{02ff`.",
|
|
"* `}`: follows in alphabetical order (or matches but is longer),",
|
|
" e.g. `id}02ff`.",
|
|
"* `#`: a comment, ignored, e.g. `dumb example#`.",
|
|
"* `!`: only passes if the *name* does *not* exist. e.g. `pnamedestination!`.",
|
|
"Every other operator except `#` fails if *name* does not exist!"
|
|
],
|
|
"usage": [
|
|
"You can use lightning-decode(7) to examine runes you have been given:",
|
|
"",
|
|
"```shell",
|
|
"lightning-cli decode tU-RLjMiDpY2U0o3W1oFowar36RFGpWloPbW9-RuZdo9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0aW1lPDE2NTY5MjA1MzgmcmF0ZT0y",
|
|
"{",
|
|
" \"type\": \"rune\",",
|
|
" \"unique_id\": \"3\",",
|
|
" \"string\": \"b54f912e33220e9636534a375b5a05a306abdfa4451a95a5a0f6d6f7e46e65da:=3&id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605&method=listpeers&pnum=1&pnameid^024b9a1fa8e006f1e393|parr0^024b9a1fa8e006f1e393&time<1656920538&rate=2\",",
|
|
" \"restrictions\": [",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605\"",
|
|
" ],",
|
|
" \"summary\": \"id (of commanding peer) equal to '024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605'\"",
|
|
" },",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"method=listpeers\"",
|
|
" ],",
|
|
" \"summary\": \"method (of command) equal to 'listpeers'\"",
|
|
" },",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"pnum=1\"",
|
|
" ],",
|
|
" \"summary\": \"pnum (number of command parameters) equal to 1\"",
|
|
" },",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"pnameid^024b9a1fa8e006f1e393\",",
|
|
" \"parr0^024b9a1fa8e006f1e393\"",
|
|
" ],",
|
|
" \"summary\": \"pnameid (object parameter 'id') starts with '024b9a1fa8e006f1e393' OR parr0 (array parameter #0) starts with '024b9a1fa8e006f1e393'\"",
|
|
" },",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"time<1656920538\"",
|
|
" ],",
|
|
" \"summary\": \"time (in seconds since 1970) less than 1656920538 (approximately 19 hours 18 minutes from now)\"",
|
|
" },",
|
|
" {",
|
|
" \"alternatives\": [",
|
|
" \"rate=2\"",
|
|
" ],",
|
|
" \"summary\": \"rate (max per minute) equal to 2\"",
|
|
" }",
|
|
" ],",
|
|
" \"valid\": true",
|
|
"}",
|
|
"```"
|
|
],
|
|
"sharing_runes": [
|
|
"Because anyone can add a restriction to a rune, you can always turn a normal rune into a read-only rune, or restrict access for 30 minutes from the time you give it to someone. Adding restrictions before sharing runes is best practice.",
|
|
"",
|
|
"If a rune has a ratelimit, any derived rune will have the same id, and thus will compete for that ratelimit. You might want to consider adding a tighter ratelimit to a rune before sharing it, so you will keep the remainder. For example, if you rune has a limit of 60 times per minute, adding a limit of 5 times per minute and handing that rune out means you can still use your original rune 55 times per minute."
|
|
],
|
|
"author": [
|
|
"Rusty Russell <<rusty@rustcorp.com.au>> wrote the original Python commando.py plugin, the in-tree commando plugin, and this manual page.",
|
|
"",
|
|
"Shahana Farooqui <<sfarooqui@blockstream.com>> is mainly responsible for migrating commando-rune to createrune."
|
|
],
|
|
"see_also": [
|
|
"lightning-commando-rune(7)",
|
|
"lightning-checkrune(7)"
|
|
],
|
|
"resources": [
|
|
"Main web site: <https://github.com/ElementsProject/lightning>"
|
|
],
|
|
"examples": [
|
|
{
|
|
"description": [
|
|
"This creates a fresh rune which can do anything:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#1",
|
|
"method": "createrune",
|
|
"params": {}
|
|
},
|
|
"response": {
|
|
"rune": "zFMd1fjhrAYxUeFA54TjloZqOt8JrA_i_nYwIgXkag49MA==",
|
|
"unique_id": "0",
|
|
"warning_unrestricted_rune": "WARNING: This rune has no restrictions! Anyone who has access to this rune could drain funds from your node. Be careful when giving this to apps that you don't trust. Consider using the restrictions parameter to only allow access to specific rpc methods."
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"We can add restrictions to that rune, like so:",
|
|
"",
|
|
"The `readonly` restriction is a short-cut for two restrictions:",
|
|
"",
|
|
"1: `['method^list', 'method^get', 'method=summary']`: You may call list, get or summary.",
|
|
"",
|
|
"2: `['method/listdatastore']`: But not listdatastore: that contains sensitive stuff!"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#2",
|
|
"method": "createrune",
|
|
"params": {
|
|
"rune": "zFMd1fjhrAYxUeFA54TjloZqOt8JrA_i_nYwIgXkag49MA==",
|
|
"restrictions": "readonly"
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "_RWaZZRI7wAYU2hqlFBmYgC_dFczcpAdI_9O87YbDpg9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl",
|
|
"unique_id": "0"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"We can do the same manually (readonly), like so:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#3",
|
|
"method": "createrune",
|
|
"params": {
|
|
"rune": "zFMd1fjhrAYxUeFA54TjloZqOt8JrA_i_nYwIgXkag49MA==",
|
|
"restrictions": [
|
|
[
|
|
"method^list",
|
|
"method^get",
|
|
"method=summary"
|
|
],
|
|
[
|
|
"method/listdatastore"
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "_RWaZZRI7wAYU2hqlFBmYgC_dFczcpAdI_9O87YbDpg9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl",
|
|
"unique_id": "0"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"This will allow the rune to be used for id starting with 0266e4598d1d3c415f57, and for the method listpeers:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#4",
|
|
"method": "createrune",
|
|
"params": {
|
|
"restrictions": [
|
|
[
|
|
"id^0266e4598d1d3c415f57"
|
|
],
|
|
[
|
|
"method=listpeers"
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "RXgu0DD_i0wSPEZkIDyZIWL0bSAGdhvJ_GHOQdTg04A9MSZpZF4wMjY2ZTQ1OThkMWQzYzQxNWY1NyZtZXRob2Q9bGlzdHBlZXJz",
|
|
"unique_id": "1"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"This will allow the rune to be used for the method pay, and for the parameter amount\\_msat to be less than 10000:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#5",
|
|
"method": "createrune",
|
|
"params": {
|
|
"restrictions": [
|
|
[
|
|
"method=pay"
|
|
],
|
|
[
|
|
"pnameamountmsat<10000"
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "QUJEYMLGgiaJvMDv_MhR2hiMKIBTbq-PrL-KxcIlirQ9MiZtZXRob2Q9cGF5JnBuYW1lYW1vdW50bXNhdDwxMDAwMA==",
|
|
"unique_id": "2"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"Let's create a rune which lets a specific peer run listpeers on themselves:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#6",
|
|
"method": "createrune",
|
|
"params": {
|
|
"restrictions": [
|
|
[
|
|
"id=0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"
|
|
],
|
|
[
|
|
"method=listpeers"
|
|
],
|
|
[
|
|
"pnum=1"
|
|
],
|
|
[
|
|
"pnameid=0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518",
|
|
"parr0=0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "jEx3l0c7NMZPSDYT7xnXXvNA83z5PDNBHRQTIk1BwNw9MyZpZD0wMjY2ZTQ1OThkMWQzYzQxNWY1NzJhODQ4ODgzMGI2MGY3ZTc0NGVkOTIzNWViMGIxYmE5MzI4M2IzMTVjMDM1MTgmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZD0wMjY2ZTQ1OThkMWQzYzQxNWY1NzJhODQ4ODgzMGI2MGY3ZTc0NGVkOTIzNWViMGIxYmE5MzI4M2IzMTVjMDM1MTh8cGFycjA9MDI2NmU0NTk4ZDFkM2M0MTVmNTcyYTg0ODg4MzBiNjBmN2U3NDRlZDkyMzVlYjBiMWJhOTMyODNiMzE1YzAzNTE4",
|
|
"unique_id": "3"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"This allows `listpeers` with 1 argument (`pnum=1`), which is either by name (`pnameid`), or position (`parr0`). We could shorten this in several ways: either allowing only positional or named parameters, or by testing the start of the parameters only. Here's an example which only checks the first 10 bytes of the `listpeers` parameter:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#7",
|
|
"method": "createrune",
|
|
"params": {
|
|
"restrictions": [
|
|
[
|
|
"id=0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"
|
|
],
|
|
[
|
|
"method=listpeers"
|
|
],
|
|
[
|
|
"pnum=1"
|
|
],
|
|
[
|
|
"pnameid^0266e4598d1d3c415f57",
|
|
"parr0^0266e4598d1d3c415f57"
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"response": {
|
|
"rune": "8_CRIJ4arWAz72A4ILOZ46MESSJtQQQ9iQZjU28qulA9NCZpZD0wMjY2ZTQ1OThkMWQzYzQxNWY1NzJhODQ4ODgzMGI2MGY3ZTc0NGVkOTIzNWViMGIxYmE5MzI4M2IzMTVjMDM1MTgmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjY2ZTQ1OThkMWQzYzQxNWY1N3xwYXJyMF4wMjY2ZTQ1OThkMWQzYzQxNWY1Nw==",
|
|
"unique_id": "4"
|
|
}
|
|
},
|
|
{
|
|
"description": [
|
|
"Before we give this to our peer, let's add two more restrictions: that it only be usable for 24 hours from now (`time<`), and that it can only be used twice a minute (`rate=2`). `date +%s` can give us the current time in seconds:"
|
|
],
|
|
"request": {
|
|
"id": "example:createrune#8",
|
|
"method": "createrune",
|
|
"params": [
|
|
"8_CRIJ4arWAz72A4ILOZ46MESSJtQQQ9iQZjU28qulA9NCZpZD0wMjY2ZTQ1OThkMWQzYzQxNWY1NzJhODQ4ODgzMGI2MGY3ZTc0NGVkOTIzNWViMGIxYmE5MzI4M2IzMTVjMDM1MTgmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjY2ZTQ1OThkMWQzYzQxNWY1N3xwYXJyMF4wMjY2ZTQ1OThkMWQzYzQxNWY1Nw==",
|
|
[
|
|
[
|
|
"time<\"$(($(date +%s) + 24*60*60))\"",
|
|
"rate=2"
|
|
]
|
|
]
|
|
]
|
|
},
|
|
"response": {
|
|
"rune": "GJb2PC-4jYslzIVz6-425bOtpkz_A_zaEhekPlrXdj09NCZpZD0wMjY2ZTQ1OThkMWQzYzQxNWY1NzJhODQ4ODgzMGI2MGY3ZTc0NGVkOTIzNWViMGIxYmE5MzI4M2IzMTVjMDM1MTgmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjY2ZTQ1OThkMWQzYzQxNWY1N3xwYXJyMF4wMjY2ZTQ1OThkMWQzYzQxNWY1NyZ0aW1lPCIkKCgkKGRhdGUgKyVzKSArIDI0KjYwKjYwKSkifHJhdGU9Mg==",
|
|
"unique_id": "4"
|
|
}
|
|
}
|
|
]
|
|
}
|