Merge 9deb067deb
into b516adbf42
This commit is contained in:
commit
b4233b3b63
|
@ -0,0 +1,255 @@
|
|||
# Remote Procedure Calls over Nostr
|
||||
|
||||
NRPC defines a Remote Procedure Call (RPC) mechanism over Nostr events. Requests and responses are expressed as signed events and routed via relays. This enables backend services to be hosted without requiring inbound network access, cloud infrastructure, or centralized API management.
|
||||
|
||||
## Problems Addressed
|
||||
|
||||
Traditional backend services typically require:
|
||||
|
||||
- A publicly reachable server (static IP, port forwarding, or tunneling).
|
||||
|
||||
- Domain names and TLS termination.
|
||||
|
||||
- Centralized API key or token management.
|
||||
|
||||
This specification removes those requirements by using Nostr’s event transport. Services can operate with only outbound relay connections, authentication is inherent in event signatures, and requests and responses can be processed asynchronously.
|
||||
|
||||
## Specification
|
||||
|
||||
### Event Kinds
|
||||
|
||||
- Request Event: kind: 22068
|
||||
|
||||
- Response Event: kind: 22069
|
||||
|
||||
### Request Event (22068)
|
||||
|
||||
- Author: Caller’s pubkey.
|
||||
|
||||
- Tags:
|
||||
|
||||
```
|
||||
["p", "<callee_pubkey>"] — target service identity.
|
||||
|
||||
["method", "<method_name>"] — method to invoke.
|
||||
|
||||
["param", "<key>", "<value>"] — optional, repeatable parameters.
|
||||
```
|
||||
|
||||
- Content: optional payload
|
||||
|
||||
### Response Event (22069)
|
||||
|
||||
- Author: Callee’s pubkey.
|
||||
|
||||
- Tags:
|
||||
|
||||
```
|
||||
["e", "<request_event_id>"] — ID of the request being answered.
|
||||
|
||||
["p", "<caller_pubkey>"] — recipient of this response.
|
||||
|
||||
["status", "<http_status_code>"] — HTTP-style status code.
|
||||
```
|
||||
|
||||
On success:
|
||||
|
||||
```
|
||||
["result", "<key>", "<value>"] — repeatable.
|
||||
|
||||
["result_json", "<json_string>"] — optional structured payload.
|
||||
```
|
||||
|
||||
On error:
|
||||
|
||||
```
|
||||
["error", "<http_status_code>", "<message>"]
|
||||
```
|
||||
|
||||
- Content: optional payload (e.g., JSON string).
|
||||
|
||||
### Examples
|
||||
|
||||
Request Example
|
||||
|
||||
```
|
||||
{
|
||||
"kind": 22068,
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"62a904c9c0e4ac1e221dc91202ee3bd98f6fd2460b619d953921108adda1af72"
|
||||
],
|
||||
[
|
||||
"method",
|
||||
"sendDM"
|
||||
]
|
||||
],
|
||||
"content": "",
|
||||
"created_at": 1758638576,
|
||||
"pubkey": "c21b1a6cdb247ccbd938dcb16b15a4fa382d00ffd7b12d5cbbad172a0cd4d170",
|
||||
"id": "aa05848fac18ecd4e0be17f4e041b808eb949963b03dd128ca7b9d610257639b",
|
||||
"sig": "8153860b5ddacceff0c89ff3d86c7e323f4e826f8415aa288d587c962ff7b209e40dace4199ff5edb21d69ab14f7f0d74328e36e48d06897d120418eaeec3e8b"
|
||||
}
|
||||
```
|
||||
|
||||
Response Example
|
||||
|
||||
```
|
||||
RESPONSE EVENT {
|
||||
kind: 22069,
|
||||
pubkey: '62a904c9c0e4ac1e221dc91202ee3bd98f6fd2460b619d953921108adda1af72',
|
||||
created_at: 1758640291,
|
||||
tags: [
|
||||
[
|
||||
'e',
|
||||
'af955bdb56977df2e0acd43791377cfcf671e334d4a5d5859af810e3c6a5cff6'
|
||||
],
|
||||
[
|
||||
'p',
|
||||
'c21b1a6cdb247ccbd938dcb16b15a4fa382d00ffd7b12d5cbbad172a0cd4d170'
|
||||
],
|
||||
[ 'status', '200' ],
|
||||
[ 'result_json', '{}' ]
|
||||
],
|
||||
content: '',
|
||||
id: 'ce1fa98bd61128beb2ba3f213086ece096eb75dc30b012d4bd2672f23415ae57',
|
||||
sig: '2647f1cad66d6a85752cbb1b07feb37077299c089bf5d1dbb79c0fd37b86ac54bbd09960d719c1b7afdcab103e148c30899b90d4367525dbb1be75f7c1c3de07',
|
||||
[Symbol(verified)]: true
|
||||
}
|
||||
```
|
||||
|
||||
## Service Introspection
|
||||
|
||||
It is recommended for services to provide a special getMethods runtime introspection of the service’s available RPC calls.
|
||||
A client may invoke it at any time to discover supported methods, required parameters, possible results, and error conditions.
|
||||
|
||||
### Request
|
||||
|
||||
A getMethods request is a standard Request Event (kind: 22068) with:
|
||||
|
||||
```
|
||||
["p", "<callee_pubkey>"]
|
||||
["method", "getMethods"]
|
||||
```
|
||||
|
||||
No additional ["param", ...] tags are required.
|
||||
|
||||
### Response
|
||||
|
||||
The callee returns a Response Event (kind: 22069) with status 200.
|
||||
Supported methods are described in repeated result tags using the following schema:
|
||||
|
||||
```
|
||||
["result", "method", "<method_name>"]
|
||||
|
||||
["result", "param", "<param_name>", "<param_type>", "<required_flag>"]
|
||||
["result", "return", "<field_name>", "<field_type>"]
|
||||
["result", "error", "<status_code>", "<description>"]
|
||||
```
|
||||
|
||||
### Real World Examples
|
||||
|
||||
### Request
|
||||
|
||||
```
|
||||
{
|
||||
"kind": 22068,
|
||||
"pubkey": "65b078fb5f4183c0538f84321ff14c0b468ded7dd45ede80d84a2ffe3a9a44dc",
|
||||
"created_at": 1758686491,
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"62a904c9c0e4ac1e221dc91202ee3bd98f6fd2460b619d953921108adda1af72"
|
||||
],
|
||||
[
|
||||
"method",
|
||||
"getMethods"
|
||||
]
|
||||
],
|
||||
"content": "",
|
||||
"id": "d15fac11a463331f342e723fe312c992ba4ec53835b86ff3418c19932d6f5acc",
|
||||
"sig": "9a8b1d0733c75475110a1dba09d59167430949f24148ee16bb431ea672412d4195f758fe3eda882f6c90032f4bdc2856653c230836988d9bec72aa918039b39c"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```
|
||||
{
|
||||
"content": "",
|
||||
"created_at": 1758686639,
|
||||
"id": "fd333cfcfb364e5d7056a9b18e7e279b45b7abca4a12c4fb7497062e619dc330",
|
||||
"kind": 22069,
|
||||
"pubkey": "62a904c9c0e4ac1e221dc91202ee3bd98f6fd2460b619d953921108adda1af72",
|
||||
"sig": "6630d10fb5792b42aac7897119b94271d85e339bc7e34a39eedcfbe2ead9e283dd7919a8311d18bb79f2c829b5184dc789e19b5479b1933f1dbcf3a7756f3599",
|
||||
"tags": [
|
||||
[
|
||||
"e",
|
||||
"e1bcf745b55bebbe33ea5f9f8a22d7f0b9bb1bb365cd41b53d936dfd7fb28734"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"4e74e8c9c9c0dee32331c2d0245542fa93bef155e9d0339e2a7fd1f86eed9c11"
|
||||
],
|
||||
["status", "200"],
|
||||
["result", "method", "createReminder"],
|
||||
["result", "param", "createReminder", "Time", "string", "required"],
|
||||
["result", "param", "createReminder", "Text", "string", "required"],
|
||||
["result", "param", "createReminder", "Date", "string", "required"],
|
||||
["result", "returns", "createReminder", "reminder_id", "string"],
|
||||
["result", "returns", "createReminder", "scheduled_at", "string"],
|
||||
["result", "returns", "createReminder","text","string"],
|
||||
["result", "returns", "createReminder", "owner", "string"],
|
||||
["result", "error", "createReminder", "400", "time and text required"],
|
||||
["result", "method","getMethods"],
|
||||
["result", "method", "sendDM"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Encrypted Requests and Responses
|
||||
|
||||
To provide end-to-end privacy and authenticated RPC over Nostr, NRPC requests and responses can be sent as rumors sealed by the sender, then giftwrapped for transport.
|
||||
|
||||
### Event Kinds
|
||||
|
||||
- Rumor Request: kind 68 (unsigned)
|
||||
|
||||
- Rumor Response: kind 69 (unsigned)
|
||||
|
||||
- Seal: kind 25
|
||||
|
||||
- Wrap: kind 21169 (signed by an ephemeral key, NIP-59 style)
|
||||
|
||||
### Flow
|
||||
|
||||
- Caller creates a rumor request (kind 68).
|
||||
|
||||
- Caller encrypts that rumor into the content of a seal event, and signs the seal with their real pubkey. The callee can later verify who it actually came from.
|
||||
|
||||
- Encrypt the seal into a giftwrap (21169) using an ephemeral key.
|
||||
|
||||
- Request giftwrap tags: ["p", "<callee_pubkey>"]
|
||||
|
||||
- Callee decrypts giftwrap → verifies seal signature → extracts rumor → processes.
|
||||
|
||||
- Callee builds a rumor response (69), seals it with their real pubkey, and publishes it inside a giftwrap (21169).
|
||||
|
||||
- Response giftwrap tags: ["e", "<request_rumor_id>"]
|
||||
|
||||
### Example Response Event
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 21169,
|
||||
"content": "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",
|
||||
"pubkey": "51e431948eb9827328d087ad93b7235a0a8e552853a53101593fdbaa569c5f2d",
|
||||
"tags": [
|
||||
["e", "d02b941a1ffdcf6c60d5100015805607020c651b784f3a795432c5bfa5b5d94d"]
|
||||
],
|
||||
"created_at": 1758712092,
|
||||
"id": "6c41facfef2b90fd6531de04b64f53e1ccd688f2b2e25a2931878bd916fded13",
|
||||
"sig": "b6c0daf9607083dd921771812f7a0cb497dd35dc8c50c6cd338475ce63fa87a01397b0902df0682cbf914d5100545311ca70dd7befb6ea4b546d90331fba960a"
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue