clone

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

docs: what the read rule covers, door by door

Dami committed 1769c59
commit
1769c59dfffc3e365cbfdf77addbb1fbc1180439
parent
f6c2b5de956159a8972516d43fdd792d69ae258b
author
Dami <dami.etoile@gmail.com>
6 files changed, +17 -13
Mdocs/01-relay-configuration.md2+-
Mdocs/03-people-and-groups.md2+-
Mdocs/05-your-relay-on-the-web.md4+++-
Mdocs/10-architecture.md4+++-
Mdocs/12-develop-extend.md2+-
Mdocs/14-http-reference.md16++++++++--------
M docs/01-relay-configuration.md +1 -1
M docs/03-people-and-groups.md +1 -1
M docs/05-your-relay-on-the-web.md +3 -1
@@ -33,6 +33,8 @@ This needs the host to have switched custom domains on. If they have not, the bl
3333 3434 Your relay stores images and files too. Who may upload follows the write rule: anyone, members or only you. The largest upload is set on the Rules tab, 25 MB by default and up to 95. Files count as files stored for fuel.3535 36+Files follow the read rule, the same as events. While reads are *anyone*, a file is a public link by hash. Under *signed in* or *members*, a download needs a Blossom `get` token or a signed request from someone the rule admits, the per-uploader list needs the same, and clients that fetch images without signing will not show them from that relay.37+3638 Clients that speak Blossom upload to `https://<name>.bind.ws` and get a link by hash. A client can ask first whether an upload would be accepted, and can have the relay copy a file from another server by URL instead of uploading it again.3739 3840 Clients that speak NIP-96 instead find the same store through `/.well-known/nostr/nip96.json`. The two doors share one bucket and one file list, so a file uploaded through either is served, listed and deleted through both. Every answer carries the file's metadata tags (NIP-94).
@@ -55,7 +57,7 @@ A view is a record the relay signs from what it holds, so a client reads one eve
5557 | zaps | sats received by the top 50 notes and authors here, from stored receipts | hourly, when something changed |5658 | presence | who has a connection open and who wrote in the last 15 minutes | live, never stored |5759 58-Profiles and relays follow the directory switch, calendar, articles and presence follow the read rule. A members-only view is never stored, since a stored event is readable by anyone the read rule lets in. It is folded and signed when a member asks for it with a signed request. Presence is an ephemeral event, kind 20078, sent to whoever subscribes to it and answered from memory at `/view/presence`; it is broadcast at most once every 30 seconds.60+Profiles and relays follow the directory switch; calendar, articles, zaps, moderation and presence follow the read rule. A members-only view is never stored, since a stored event is readable by anyone the read rule lets in. It is folded and signed when a member asks for it with a signed request. Presence is an ephemeral event, kind 20078, sent to whoever subscribes to it and answered from memory at `/view/presence`; it is broadcast at most once every 30 seconds.5961 6062 Views cost rows written, which fuel counts. The Views tab shows what each one wrote on its last run, and the weekly digest adds them up. Any view can be switched off there.6163
M docs/10-architecture.md +3 -1
@@ -43,7 +43,9 @@ Custom hostnames (`domains.ts`) are registered with Cloudflare for SaaS from the
4343 4444 The constructor runs schema setup inside `blockConcurrencyWhile`, then handles fetches. Websockets use the hibernation API. Per-connection state (NIP-42 challenge, authenticated pubkeys, open subscriptions, the client's address) is stored on the socket with `serializeAttachment`, so it survives hibernation. Negentropy sessions are in memory only and end with `closed:` when the object sleeps.4545 46-The write path is one gate plus the client policy. The gate refuses malformed, expired, banned and out-of-context events (an `h` tag for another group) and, for client writes, the unclaimed and expired-lease states, fuel and NIP-70. The client policy adds kind rules, retention windows, the write rule, proof of work and per-member caps. Host-side writes, such as pulls and the relay's own records, pass the gate only. Kind 24133, NIP-46 signer traffic, passes everything but bans and rate limits: it is ephemeral, never stored and encrypted end to end, so the relay can carry a remote signer's session for the console, including for someone about to claim it.46+The write path is one gate plus the client policy. The gate refuses malformed, expired, banned and out-of-context events (an `h` tag for another group) and, for client writes, the unclaimed and expired-lease states, fuel and NIP-70. The client policy adds kind rules, retention windows, the write rule, proof of work and per-member caps. Host-side writes, such as pulls and the relay's own records, pass the gate only. Kind 24133, NIP-46 signer traffic, passes everything but bans and rate limits on the way in: it is ephemeral, never stored and encrypted end to end, so the relay can carry a remote signer's session for the console, including for someone about to claim it. On the way out it is a private kind like a gift wrap: delivered only to a socket authenticated as its sender or its `p` recipient. A subscription to 24133 alone may be opened under any read rule, so a signer can subscribe before its AUTH lands; it receives nothing until it does. The console's remote-signer client answers the challenge with the session's own key.47+48+The read path is one gate too, `Settings.mayRead`, given the pubkeys a caller has proved: on the socket the NIP-42 set, over HTTP whatever `whoAsks` (`auth.ts`) found in the Authorization header, a NIP-98 signature or a Blossom token. REQ, COUNT, NEG-OPEN, the bridge, blob downloads and listings, file reports, the same-host file copy and the members-only views all ask it; the directory switch adds `mayList` for names and counts. Changing the rule runs `enforceReads`, which closes every open subscription the rule no longer admits.4749 4850 ## Storage and media4951
M docs/12-develop-extend.md +1 -1
M docs/14-http-reference.md +8 -8
@@ -5,7 +5,7 @@ audience: integrator
55 66 # HTTP reference77 8-Every path the worker and a relay answer. Paths are on `https://<name>.bind.ws` unless marked apex. Auth column: "NIP-98" is a signed request (see [Scripts and agents](13-scripts-and-agents.md)); "Blossom" is a kind 24242 token in the Authorization header; "none" is public. JSON errors carry `{ "error": "<prefix>: reason" }`; Blossom and NIP-96 errors also set an `X-Reason` header.8+Every path the worker and a relay answer. Paths are on `https://<name>.bind.ws` unless marked apex. One read rule covers every door that shows events, files, names or presence: where the auth column says "the read rule", a relay whose reads are *anyone* answers without a signature, one whose reads are *signed in* takes any valid signature, and one whose reads are *members* takes a member's. Auth column: "NIP-98" is a signed request (see [Scripts and agents](13-scripts-and-agents.md)); "Blossom" is a kind 24242 token in the Authorization header; "none" is public. JSON errors carry `{ "error": "<prefix>: reason" }`; Blossom and NIP-96 errors also set an `X-Reason` header.99 1010 Any request with `Accept: application/nostr+json` answers the NIP-11 document, whatever the path. A websocket upgrade on any path opens the relay. A blocked address is refused with 403 on the socket and on the doors that write, read or serve files; the page, NIP-11 and management stay open.1111
@@ -47,9 +47,9 @@ Signed with NIP-98 over the exact URL, method and body. Same gates as a socket:
4747 | `/upload` | HEAD | Blossom `upload` | whether an upload with the `X-SHA-256`, `X-Content-Type` and `X-Content-Length` headers would be accepted | 200; 400 bad headers; 401; 403 the write gate; 411 no length; 413 too big |4848 | `/upload` | PUT | Blossom `upload` | the descriptor with `nip94` tags | 200 exists; 201 stored; 400; 401; 403 gate or a removed hash; 413 |4949 | `/mirror` | PUT | Blossom `upload` | copies the blob at `{ "url": ... }` from another server, the descriptor | 200 exists; 201 stored; 400; 401; 403; 409 hash mismatch with the token; 413; 502 origin failed |50-| `/report` | PUT | none, the body is a signed kind 1984 | files a report for each `x` tag the relay holds | 200; 400 bad event or older than an hour; 403 unclaimed relay or banned reporter; 404 no such blob |51-| `/list/<pubkey>` | GET | none | that uploader's descriptors | 200; 400 bad pubkey |52-| `/<sha256>[.ext]` | GET, HEAD | none | the blob, ranges honoured | 200; 206; 404 |50+| `/report` | PUT | none, the body is a signed kind 1984; the reporter must pass the read rule | files a report for each `x` tag the relay holds | 200; 400 bad event or older than an hour; 401 reporter must sign in under a read rule; 403 unclaimed relay, banned reporter or reporter not admitted by the read rule; 404 no such blob |51+| `/list/<pubkey>` | GET | the read rule: none while reads are open, else Blossom `list` or NIP-98 | that uploader's descriptors | 200; 400 bad pubkey; 401; 403 |52+| `/<sha256>[.ext]` | GET, HEAD | the read rule: none while reads are open, else Blossom `get` or NIP-98 | the blob, ranges honoured; not cacheable by shared caches when gated | 200; 206; 401; 403; 404 |5353 | `/<sha256>` | DELETE | Blossom `delete` | removes it; the uploader or the owner | 204; 400 token names another blob; 401; 403; 404 |5454 5555 ## Files: NIP-96
@@ -61,7 +61,7 @@ The same bucket and file list through the NIP-96 shape. Answers are `{ status, m
6161 | `/.well-known/nostr/nip96.json` | GET | none | `api_url`, `download_url`, `supported_nips`, content types, the free plan with the size cap | 200 |6262 | `/nip96` | POST, multipart with a `file` field, optional `size` and `caption` | NIP-98, `payload` is the file's hash | the NIP-94 event in `nip94_event` and the Blossom URL | 200; 400; 401; 403; 413 |6363 | `/nip96?page=&count=` | GET | NIP-98 | the caller's files | 200; 401 |64-| `/nip96/<sha256>` | GET, HEAD | none | the blob, same as the Blossom path | 200; 404 |64+| `/nip96/<sha256>` | GET, HEAD | the read rule, as the Blossom path | the blob, same as the Blossom path | 200; 401; 403; 404 |6565 | `/nip96/<sha256>` | DELETE | NIP-98 | removes it; the uploader or anyone with the storage action | 200; 401; 403; 404 |6666 6767 ## Pages and feed
@@ -88,7 +88,7 @@ Only while reads are open. Otherwise every path here answers 404, and an unclaim
8888 8989 | Path | Method | Auth | Answers | Status |9090 |---|---|---|---|---|91-| `/.well-known/nostr.json?name=` | GET | none | NIP-05 for members with a name, with this relay as their relay | 200 |91+| `/.well-known/nostr.json?name=` | GET | none for a name; the directory switch for the listing without one, else NIP-98 by a member | NIP-05 for the member with that name, with this relay as their relay; without a name, every named member when the directory is public | 200; 401 bad signature |9292 | `/invite/<code>` | GET | none | the invite page, with the join terms | 200 |9393 | `/api/join-policy` | GET | none | `{ terms }` | 200 |9494 | `/api/invites/claim` | POST `{ "code": ... }` | NIP-98 | `{ status: "joined" }` or `{ status: "already_member", role }` | 200; 400; 401; 403 invalid, used up or expired, or banned |
@@ -103,14 +103,14 @@ Only while reads are open. Otherwise every path here answers 404, and an unclaim
103103 104104 | Path | Method | Auth | Answers | Status |105105 |---|---|---|---|---|106-| `/fuel` | GET | none | meters, allowances, prices, balance and recent zaps | 200 |106+| `/fuel` | GET | none | meters, allowances, prices and balance; who zapped is in the `stats` management method | 200 |107107 | `/fuel/invoice` | POST `{ "zapRequest": <kind 9734> }` | none | `{ invoice, providerPubkey, msats }` from the lightning provider | 200; 400 bad request or amount; 502 provider |108108 109109 ## Views110110 111111 | Path | Method | Auth | Answers | Codes |112112 |---|---|---|---|---|113-| `/view/<name>` | GET | none for a public view; NIP-98 by a member for a members-only one | the view's latest signed kind 30078 record as JSON, or presence as a kind 20078 from memory | 200; 401 `auth-required:` when a members-only view is asked for without a signature; 403 when the signer is not a member; 404 when the view is off, unknown, or has not run yet |113+| `/view/<name>` | GET | none for a public view; NIP-98 by a member for a members-only one; `zaps`, `moderation`, `calendar`, `articles` and `presence` follow the read rule, `profiles` and `relays` the directory switch | the view's latest signed kind 30078 record as JSON, or presence as a kind 20078 from memory | 200; 401 `auth-required:` when a members-only view is asked for without a signature; 403 when the signer is not a member; 404 when the view is off, unknown, or has not run yet |114114 115115 Names: `profiles`, `relays`, `calendar`, `moderation`, `articles`, `zaps`, `presence`. The information document lists the ones a relay keeps under `views`, with each one's kind, `d`, trigger and audience.116116