clone

nostr relays on demand at <name>.bind.ws: claim one with your key, pay in zaps past the free allowance

docs: scripts and agents, the path from lease to handover and every method by action

Dami committed 39b1150
commit
39b1150223b6748efe9b49d993766e9208c3902a
parent
7101ea6112bd637c593b25866c215ee5e2647b5c
author
Dami <dami.etoile@gmail.com>
1 file changed, +168 -0
Adocs/13-scripts-and-agents.md168++++++++++++++++++++++++++++++
A docs/13-scripts-and-agents.md +168 -0
@@ -0,0 +1,168 @@
1+---2+title: Scripts and agents3+audience: integrator4+---5+6+# Scripts and agents7+8+A relay for a script, an agent or a service, from nothing to handover, without a browser. Everything the console does is a signed HTTP call, so a key is all a program needs.9+10+## The path11+12+1. **Lease a name.** `POST https://bind.ws/lease` answers with a memorable name, its websocket URL, its console URL and when it expires (14 days). Anyone can read and write to a lease. Sign the request with your key (NIP-98, below) and the lease is reserved: only that key can claim it.13+2. **Claim it.** Send the `claim` method (NIP-86, below) to the relay's console URL. The relay converts in place: the events stay, your key is the owner. A lease starts with open writes and a 14-day keep-for rule; `resetrules` restores the defaults, or `applypreset` sets a bundle.14+3. **Publish and query.** `POST /events` with an event, `POST /query` or `POST /count` with a list of filters, each signed with NIP-98. The signer has the same standing as an authenticated socket, so the owner's own reads pass any read rule.15+4. **Bring history in.** `backfill` pulls your own events from the relays in your kind 10002 stored here, or from a list you give. `pullfrom` copies another relay. `listjobs` and `pullstatus` report on them.16+5. **Read the summary.** `GET /card.json` is the machine-readable state of a name: owner, rules, fuel, member count when public and the group address. `GET /card.nostr` is the same, signed by the relay's key.17+6. **Hand it over.** `transferowner` gives the relay to a member, so an agent can set a relay up and pass it to its human. The old owner stays on as a moderator.18+19+## Signing a request (NIP-98)20+21+Every management call and every door that needs a key takes an `Authorization: Nostr <base64 event>` header. The event is:22+23+| Field | Value |24+|---|---|25+| `kind` | 27235 |26+| `created_at` | now; the relay allows 60 seconds either way |27+| tag `u` | the full URL, host and path must match the request |28+| tag `method` | the HTTP method |29+| tag `payload` | the hex SHA-256 of the body, required whenever there is a body |30+31+Encode the signed event as base64 JSON. The relay answers `401` with an `auth-required:` reason when any of that is off.32+33+With curl and [nak](https://github.com/fiatjaf/nak):34+35+```36+BODY='{"method":"claim","params":[]}'37+TOKEN=$(nak event -k 27235 -t u=https://brave-otter.bind.ws/ -t method=POST \38+ -t payload=$(printf '%s' "$BODY" | shasum -a 256 | cut -d' ' -f1) --sec "$NSEC" | base64)39+curl -X POST https://brave-otter.bind.ws/ \40+ -H "Authorization: Nostr $TOKEN" -H "content-type: application/nostr+json+rpc" -d "$BODY"41+```42+43+With node and nostr-tools:44+45+```js46+import { finalizeEvent, generateSecretKey } from "nostr-tools/pure";47+import { getToken } from "nostr-tools/nip98";48+49+const sk = generateSecretKey();50+const sign = (e) => finalizeEvent(e, sk);51+52+// A reserved lease: the signature names the key that may claim it.53+const leaseURL = "https://bind.ws/lease";54+const lease = await (await fetch(leaseURL, { method: "POST", headers: { authorization: await getToken(leaseURL, "POST", sign, true) } })).json();55+56+// Management calls go to the relay's root as JSON-RPC.57+async function rpc(method, ...params) {58+ const body = { method, params };59+ const token = await getToken(lease.console, "POST", sign, true, body);60+ const r = await fetch(lease.console, { method: "POST", headers: { authorization: token, "content-type": "application/nostr+json+rpc" }, body: JSON.stringify(body) });61+ return r.json();62+}63+64+await rpc("claim"); // { result: { owner, claimed: true, converted: true } }65+await rpc("applypreset", "outbox"); // one call sets writes, reads, kinds and keep-for66+await rpc("backfill", ["wss://relay.damus.io", "wss://nos.lol"]);67+console.log(await rpc("listjobs"));68+```69+70+The bridge takes the same header. `POST /events` answers `{ event_id, accepted, message }`; `POST /query` answers a list of events; `POST /count` answers `{ count }`. The bridge is rate limited per address at four times the per-connection allowance and answers `429` past it.71+72+## Management calls (NIP-86)73+74+`POST /` with `content-type: application/nostr+json+rpc` and a body of `{ "method": "...", "params": [...] }`. Answers are `{ result }` or `{ error }` with a reason that starts with `invalid:`, `restricted:`, `unsupported:` or `error:`. Each method needs an action; the owner has every action and a moderator has read, members, ban, delete events, invites and reports. `supportedmethods` lists the names.75+76+**Open to anyone**77+78+- `claim`: take an unclaimed relay, or convert a lease you hold. Answers the owner and `converted: true` when it was a lease.79+80+**Read**81+82+- `stats`: counts, connections, name, owner and fuel.83+- `getpolicy`: the whole policy.84+- `listpresets`: preset names, descriptions and whether one needs a source.85+- `listmembers`, `listpeople`, `listallowedpubkeys`: the member list in three shapes.86+- `listbannedpubkeys`, `listbannedevents`, `listblockedips`: the bans.87+- `listinvites`: live invites; a member under the invite rule sees only their own.88+- `listrecentevents [limit]`: the newest events.89+- `listallowedkinds`, `listblockedkinds`, `listretention`: the kind and keep-for rules.90+- `listblobs`, `listreports`: files and the reports queue.91+- `successionstatus`: last seen, days silent, warning state and the handover log; the heir may read it too.92+93+**Members**94+95+- `setmember pubkey {name?, note?, role?, keepDays?, maxBytes?}`: add or edit. Only the owner sets roles and limits.96+- `allowpubkey pubkey reason`: the NIP-86 spelling of add.97+- `removemember pubkey`, `unrulepubkey pubkey`: remove a member or lift a ban.98+- `removesubtree pubkey`: remove a member and everyone they invited, stopping at moderators.99+100+**Ban**101+102+- `banpubkey pubkey reason`, `banevent id reason`, `allowevent id`.103+- `blockip address reason`, `unblockip address`.104+105+**Delete events**106+107+- `deleteevent id`: remove one event.108+109+**Invites**110+111+- `createinvite ttlSeconds maxUses note`: mint a code. A member under the invite rule may mint within their quota.112+- `revokeinvite code`.113+114+**Reports**115+116+- `resolvereport id action`: dismiss, delete or ban.117+- `listeventsneedingmoderation`: one entry per open reported thing, event id or blob hash.118+119+**Rules**120+121+- `setpolicy {…}`: any policy fields; the relay drops values it does not accept.122+- `allowkind k`, `disallowkind k`, `unrulekind k`.123+- `setretention kind|null days`, `purgekind kind|null days`.124+- `resetrules`: the defaults, kind rules and keep-for cleared.125+- `applypreset name {source?}`: a bundle; `search` needs a source, `articles` takes one.126+127+**Identity**128+129+- `changerelayname`, `changerelaydescription`, `changerelayicon`: one string each.130+- `adddomain host`, `checkdomain host`, `removedomain host`, `listdomains`: custom hostnames, when the host has them on.131+- `notifytest`: a test message to the owner.132+133+**Storage**134+135+- `storagestats`: bytes by kind, files, retention.136+- `deleteblob sha256`.137+- `listdumps`, `dumpnow`, `deletedump name`.138+139+**Config**140+141+- `exportconfig`, `importconfig document`: rules, identity, members, bans, kind rules and retention as one document.142+143+**Jobs**144+145+- `pullfrom url`, `pullstatus`: copy one relay and follow it.146+- `addjob {kind, relays, filter?, every?, label?}`: a `pull` or `push`, once or every 1, 6 or 24 hours. Up to 10 relays; filters take up to 50 authors and 50 kinds and a `since`.147+- `removejob id`, `runjob id`, `listjobs`.148+- `backfill [relays?]`: your own events from your kind 10002 here, or from the list.149+150+**Transfer**151+152+- `transferowner pubkey`: to a member; you stay as a moderator.153+- `setsuccession {heir, afterDays}` with 90, 180 or 365, `clearsuccession`.154+155+**Fork**156+157+- `forkrelay {name?, holder?, filter?, people?}`: a new leased name filled from this relay, reserved for the holder. One an hour.158+159+**Delete**160+161+- `deleterelay name`: the relay's own name, typed, is the confirmation.162+163+## Limits worth knowing164+165+- Leases: five a minute per address and 60 a minute overall at the apex.166+- Bridge and sockets: the relay's events and queries per minute, per connection, and four times that per address.167+- Fuel: a lease cannot be zapped and runs on the free allowance only. A claimed relay past an allowance with no balance goes read-only until zapped; `GET /fuel` reports the meters.168+- A relay only pulls from sources that let anyone read, and only pushes to targets that accept its key's writes.