diff --git a/README.md b/README.md index 0d44553..87e720d 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Nostrbook +# nbread.lol Nostr-native blogging platform. Your posts are [NIP-23](https://github.com/nostr-protocol/nips/blob/master/23.md) -`kind 30023` events signed by **your** key — Nostrbook mirrors them into a fast -edge database and renders them beautifully at `you.nostrbook.net`. +`kind 30023` events signed by **your** key — nbread.lol mirrors them into a fast +edge database and renders them beautifully at `you.nbread.lol`. -- **Own your words**: every post is a signed Nostr event; Nostrbook is just a +- **Own your words**: every post is a signed Nostr event; nbread.lol is just a renderer. Take your key elsewhere any time and your blog comes with you. - **No accounts, no passwords**: log in with a NIP-07 browser extension. - **Fast + minimal**: one Cloudflare Worker, server-side rendering, no @@ -37,5 +37,5 @@ Phase-by-phase build plan: see [docs/phases/](docs/phases/). Copyright (C) 2026 sovITxyz -[AGPL-3.0-only](LICENSE). If you run a modified Nostrbook as a service, you +[AGPL-3.0-only](LICENSE). If you run a modified nbread.lol as a service, you must offer its source to your users. diff --git a/docs/ops.md b/docs/ops.md index 281f9b7..2c13b81 100644 --- a/docs/ops.md +++ b/docs/ops.md @@ -1,4 +1,4 @@ -# Nostrbook operations (P7) +# nbread.lol operations (P7) Hardening + launch reference: security headers, the full per-endpoint rate-limit/cache inventory, WAF dashboard settings, free-tier incident notes, @@ -13,8 +13,8 @@ every Worker response is stamped — guard 404s (unknown/spoofed hosts), tenant | Class | Applies to | CSP | Extra | | ----- | ---------- | --- | ----- | -| **Blog** | `.nostrbook.net` (all paths) AND apex `/npub1…` views | `default-src 'none'; img-src * data:; style-src 'self' 'unsafe-inline'; media-src *; base-uri 'none'; form-action 'none'` | no XFO (embeddable **by design** — see notes) | -| **Apex** | everything else on `nostrbook.net` (+ unknown-host 404s) | `default-src 'none'; script-src 'self' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src * data:; media-src *; connect-src 'self' wss:; frame-src https://challenges.cloudflare.com; form-action 'self'; base-uri 'none'; frame-ancestors 'none'` | `X-Frame-Options: DENY` | +| **Blog** | `.nbread.lol` (all paths) AND apex `/npub1…` views | `default-src 'none'; img-src * data:; style-src 'self' 'unsafe-inline'; media-src *; base-uri 'none'; form-action 'none'` | no XFO (embeddable **by design** — see notes) | +| **Apex** | everything else on `nbread.lol` (+ unknown-host 404s) | `default-src 'none'; script-src 'self' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src * data:; media-src *; connect-src 'self' wss:; frame-src https://challenges.cloudflare.com; form-action 'self'; base-uri 'none'; frame-ancestors 'none'` | `X-Frame-Options: DENY` | Both classes always send `X-Content-Type-Options: nosniff` and `Referrer-Policy: strict-origin-when-cross-origin`. @@ -63,7 +63,7 @@ closed** (a D1 error denies). Denied requests still count. Sessions are permissionless (anyone with a keypair can mint one), so per-pubkey limits are abuse bounds, not politeness. -### Apex (nostrbook.net) +### Apex (nbread.lol) | Endpoint | Limiter (key → max/window) | Cache | Per-request cost / bound | | -------- | -------------------------- | ----- | ------------------------ | @@ -89,7 +89,7 @@ abuse bounds, not politeness. CSRF (Origin / Sec-Fetch-Site same-origin proof) covers **every** unsafe method on the apex, `/admin` and `/api` included. -### Blog subdomains (`.nostrbook.net`) +### Blog subdomains (`.nbread.lol`) | Endpoint | Limiter | Cache | Per-request cost | | -------- | ------- | ----- | ---------------- | @@ -114,7 +114,7 @@ per run. Nothing else writes KV. P7 added **no new KV write classes** — admin bumps are ordinary gen bumps. -## 3. WAF setup (Cloudflare dashboard, zone `nostrbook.net`) +## 3. WAF setup (Cloudflare dashboard, zone `nbread.lol`) Application-level limits above bound *single-source* abuse; the WAF is the *distributed/volumetric* backstop (and keeps scanner noise off the Worker @@ -129,7 +129,7 @@ them.** Configure once after Gate B: - **Rule name**: `global-per-ip-throttle` - **If incoming requests match**: preferred expression - `(http.host eq "nostrbook.net") or (http.host wildcard "*.nostrbook.net")`. + `(http.host eq "nbread.lol") or (http.host wildcard "*.nbread.lol")`. **Free-plan caveat**: rate-limiting rule expressions restrict the field set (`http.host` and the `wildcard` operator are plan-gated on some accounts). If the dashboard rejects it, use the guaranteed-configurable fallback: the @@ -242,17 +242,17 @@ on the release commit, and `bash scripts/smoke.sh local` green. ### Secrets & bindings checklist (once per account) ```sh -wrangler d1 create nostrbook # paste database_id into wrangler.jsonc +wrangler d1 create nbread # paste database_id into wrangler.jsonc wrangler kv namespace create KV # paste id into wrangler.jsonc -wrangler d1 migrations apply nostrbook --remote +wrangler d1 migrations apply nbread --remote wrangler secret put TURNSTILE_SECRET_KEY # from the Turnstile widget (see below) wrangler secret put ADMIN_PUBKEY # OPTIONAL — omit to launch with /admin disabled ``` -- Create the Turnstile widget (dashboard → Turnstile) for `nostrbook.net` +- Create the Turnstile widget (dashboard → Turnstile) for `nbread.lol` **and add the workers.dev preview hostname** (e.g. - `nostrbook..workers.dev`) to the widget's hostnames so Gate A can + `nbread..workers.dev`) to the widget's hostnames so Gate A can exercise the claim flow. Put the site key in `wrangler.jsonc` `vars.TURNSTILE_SITE_KEY`. - `vars.ENVIRONMENT` stays `"production"` in the committed config (the @@ -260,47 +260,47 @@ wrangler secret put ADMIN_PUBKEY # OPTIONAL — omit to launch with /a ### Gate A — workers.dev preview -The zone `nostrbook.net` must already exist on the account (add it per +The zone `nbread.lol` must already exist on the account (add it per `docs/setup.md` §1 — routes can attach while DNS still points elsewhere; user traffic is unaffected because no DNS records exist yet). ```sh # Deploy with MAIN_HOST overridden to the preview host so the host guard -# treats workers.dev as the apex (the committed var stays nostrbook.net): -npx wrangler deploy --var MAIN_HOST:nostrbook..workers.dev +# treats workers.dev as the apex (the committed var stays nbread.lol): +npx wrangler deploy --var MAIN_HOST:nbread..workers.dev # Smoke vs the preview URL (subdomain checks auto-skip on workers.dev): -bash scripts/smoke.sh https://nostrbook..workers.dev +bash scripts/smoke.sh https://nbread..workers.dev ``` Gate A passes when: CI is green, preview smoke is green, and a manual login → claim → publish loop works on the preview (needs a NIP-07 extension; see the manual-check notes at the bottom of `scripts/smoke.sh`). -### Gate B — nostrbook.net live +### Gate B — nbread.lol live 1. **DNS** (per `docs/setup.md` §2): apex `A @ 192.0.2.1` **Proxied** + - wildcard `CNAME * nostrbook.net` **Proxied**. SSL/TLS **Full (strict)**; - confirm Universal SSL covers `nostrbook.net` + `*.nostrbook.net`. + wildcard `CNAME * nbread.lol` **Proxied**. SSL/TLS **Full (strict)**; + confirm Universal SSL covers `nbread.lol` + `*.nbread.lol`. Then, under SSL/TLS → **Edge Certificates**: enable **Always Use HTTPS** and **HSTS** with `max-age` ≥ 6 months (15552000). Sessions are Secure host-only cookies on the apex; without zone HSTS a first-visit `http://` navigation is interceptable before the 301. Hold off `includeSubDomains`/preload until tenant subdomains are confirmed stable on TLS (a preloaded broken wildcard is unrecoverable for months). -2. **Deploy the committed config** (restores `MAIN_HOST=nostrbook.net`): +2. **Deploy the committed config** (restores `MAIN_HOST=nbread.lol`): ```sh npx wrangler deploy ``` -3. Verify both routes (`nostrbook.net/*`, `*.nostrbook.net/*`) appear under +3. Verify both routes (`nbread.lol/*`, `*.nbread.lol/*`) appear under the zone's Workers Routes page and the cron shows under the Worker's Triggers tab. 4. **Smoke vs prod** (includes subdomain + header checks): ```sh - bash scripts/smoke.sh https://nostrbook.net + bash scripts/smoke.sh https://nbread.lol ``` 5. **Observe the cron**: `npx wrangler tail --format=pretty` across one @@ -308,8 +308,8 @@ see the manual-check notes at the bottom of `scripts/smoke.sh`). 6. **Acceptance**: claim a test blog with a real NIP-07 key (login → claim → publish via the editor, or publish a NIP-23 post from any Nostr client and wait ≤15 min for the cron) and confirm - `https://.nostrbook.net/` renders the relay content, RSS - validates, and `https://nostrbook.net/.well-known/nostr.json?name=` + `https://.nbread.lol/` renders the relay content, RSS + validates, and `https://nbread.lol/.well-known/nostr.json?name=` answers. 7. **WAF**: configure §3 (rate rule + scanner-path block). The preferred expressions use fields/functions whose free-plan availability varies by diff --git a/docs/phases/CONTRACTS.md b/docs/phases/CONTRACTS.md index 3730c0d..a5df45e 100644 --- a/docs/phases/CONTRACTS.md +++ b/docs/phases/CONTRACTS.md @@ -20,7 +20,7 @@ test/{unit,integration}/**.spec.ts test/fixtures/ scripts/{gen-fixtures.ts,sm docs/phases/P0.md..P7.md # these briefs, copied from this plan ``` -**Bindings** (`wrangler.jsonc`): D1 `DB`; KV `KV`; vars `MAIN_HOST=nostrbook.net`, `ENVIRONMENT`, `TURNSTILE_SITE_KEY`, `RELAYS` (comma list: wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band); secrets `TURNSTILE_SECRET_KEY`; cron `*/15 * * * *`; routes `nostrbook.net/*` + `*.nostrbook.net/*` zone `nostrbook.net`; assets dir `public/` binding `ASSETS`. +**Bindings** (`wrangler.jsonc`): D1 `DB`; KV `KV`; vars `MAIN_HOST=nbread.lol`, `ENVIRONMENT`, `TURNSTILE_SITE_KEY`, `RELAYS` (comma list: wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band,wss://relay.primal.net,wss://nostr.wine,wss://purplepag.es,wss://relay.snort.social,wss://nostr.mom); secrets `TURNSTILE_SECRET_KEY`; cron `*/15 * * * *`; routes `nbread.lol/*` + `*.nbread.lol/*` zone `nbread.lol`; assets dir `public/` binding `ASSETS`. **D1 schema** (migrations/0001_init.sql): ```sql @@ -69,7 +69,7 @@ renderPost(md: string): string // markdown-it + strict sanitizer; NO raw **Fixtures** (`test/fixtures/`, generated once by `scripts/gen-fixtures.ts` with nostr-tools, committed): 3 test keypairs (alice, bob, mallory), signed kind 0 + kind 30023 events (valid set incl. markdown torture + XSS payloads in content), tampered variants (bad sig / bad id / wrong pubkey), a kind 5 delete, stale-vs-newer replaceable pairs. All tests use these — never generate keys at test time. -**Testing rules (every phase)**: implementer ships unit tests with the code; routes get `SELF.fetch` integration tests with Host overrides (`nostrbook.net`, `alice.nostrbook.net`, `unknown.nostrbook.net`, `nostrbook.net.evil.com`); rendered HTML/XML gets snapshot tests; `scripts/smoke.sh ` grows each phase and must pass; CI = typecheck + vitest. +**Testing rules (every phase)**: implementer ships unit tests with the code; routes get `SELF.fetch` integration tests with Host overrides (`nbread.lol`, `alice.nbread.lol`, `unknown.nbread.lol`, `nbread.lol.evil.com`); rendered HTML/XML gets snapshot tests; `scripts/smoke.sh ` grows each phase and must pass; CI = typecheck + vitest. ## Addendum (P0, orchestrator-approved) @@ -112,7 +112,7 @@ Ratified in full: the `delete_horizons` schema change (migration 0004), the sett - **Protective leading newline in the editor content and dashboard CSS textareas** (`{"\n" + value}`): the HTML parser eats exactly one newline after ` diff --git a/src/views/main/discover.tsx b/src/views/main/discover.tsx index 99f46e3..27779d0 100644 --- a/src/views/main/discover.tsx +++ b/src/views/main/discover.tsx @@ -17,7 +17,7 @@ export function DiscoverPage(props: { error?: string | null; }) { return ( - +

Discover

diff --git a/src/views/main/docs.tsx b/src/views/main/docs.tsx index 4bfb267..2c8e8d1 100644 --- a/src/views/main/docs.tsx +++ b/src/views/main/docs.tsx @@ -5,19 +5,19 @@ import { SiteHeader, SiteFooter } from "./chrome"; export function DocsPage() { return (

Docs

Last updated: 2026-07-14

-

What is Nostrbook

+

What is nbread.lol

Posts are NIP-23 long-form events (kind 30023) on public Nostr - relays. Nostrbook mirrors them into an edge database and renders - them at handle.nostrbook.net. + relays, which nbread.lol mirrors into an edge database and renders + at handle.nbread.lol.

Signing in

@@ -43,14 +43,14 @@ export function DocsPage() {

Your blog

- Your blog lives at handle.nostrbook.net, with{" "} + Your blog lives at handle.nbread.lol, with{" "} /rss.xml, /atom.xml, and{" "} /sitemap.xml.

NIP-05

- You are handle@nostrbook.net. + You are handle@nbread.lol.

Markdown support

@@ -88,14 +88,14 @@ body { font-family: Georgia, serif; }

Unclaimed blogs

- Any npub renders read-only at nostrbook.net/npub1…. + Any npub renders read-only at nbread.lol/npub1….

Source & license

AGPL-3.0 —{" "} - - github.com/sovITxyz/nostrbook + + github.com/sovITxyz/nbread . Run your own if you like.

diff --git a/src/views/main/editor.tsx b/src/views/main/editor.tsx index 3f842f1..a4dbc8f 100644 --- a/src/views/main/editor.tsx +++ b/src/views/main/editor.tsx @@ -90,7 +90,7 @@ export function EditorPage(props: { return ( diff --git a/src/views/main/home.tsx b/src/views/main/home.tsx index 2b1fc98..19fa03e 100644 --- a/src/views/main/home.tsx +++ b/src/views/main/home.tsx @@ -9,19 +9,19 @@ import { SiteFooter } from "./chrome"; export function MainHome() { return (
-

Nostrbook

+

nbread.lol

Nostr-native, no-nonsense, super-fast blogging.

Your posts are NIP-23 events signed by your key — we just render - them beautifully at you.nostrbook.net. + them beautifully at you.nbread.lol.

@@ -41,11 +41,11 @@ export function MainHome() {

  • A real blog, not a feed — a clean site at{" "} - handle.nostrbook.net with RSS, Atom, and a sitemap. + handle.nbread.lol with RSS, Atom, and a sitemap.
  • Free NIP-05 — be{" "} - handle@nostrbook.net everywhere on Nostr. + handle@nbread.lol everywhere on Nostr.
  • Write anywhere — the built-in markdown editor diff --git a/src/views/main/login.tsx b/src/views/main/login.tsx index 21584d4..760077f 100644 --- a/src/views/main/login.tsx +++ b/src/views/main/login.tsx @@ -7,7 +7,7 @@ import { SiteHeader, SiteFooter } from "./chrome"; */ export function LoginPage() { return ( - +

    Sign in

    diff --git a/src/views/main/not-found.tsx b/src/views/main/not-found.tsx index 6d4d43a..312324e 100644 --- a/src/views/main/not-found.tsx +++ b/src/views/main/not-found.tsx @@ -4,7 +4,7 @@ import { SiteHeader, SiteFooter } from "./chrome"; /** Apex 404 page (mainApp.notFound). Static JSX — safe on any unknown path. */ export function MainNotFound() { return ( - +

    404

    diff --git a/src/views/main/privacy.tsx b/src/views/main/privacy.tsx index 74ee9c8..01bec3a 100644 --- a/src/views/main/privacy.tsx +++ b/src/views/main/privacy.tsx @@ -5,8 +5,8 @@ import { SiteHeader, SiteFooter } from "./chrome"; export function PrivacyPage() { return (
    @@ -67,9 +67,9 @@ export function PrivacyPage() {

    Source

    - Nostrbook is open source under AGPL-3.0:{" "} - - github.com/sovITxyz/nostrbook + nbread.lol is open source under AGPL-3.0:{" "} + + github.com/sovITxyz/nbread .

    @@ -78,7 +78,7 @@ export function PrivacyPage() {

    Questions about this policy:{" "} git@sovit.xyz or open an issue on{" "} - GitHub. + GitHub.

    diff --git a/src/views/main/search.tsx b/src/views/main/search.tsx index f29b133..1328030 100644 --- a/src/views/main/search.tsx +++ b/src/views/main/search.tsx @@ -18,7 +18,7 @@ export function SearchPage(props: { error?: string | null; }) { return ( - +

    Search

    diff --git a/src/views/main/terms.tsx b/src/views/main/terms.tsx index 82efc03..44884fc 100644 --- a/src/views/main/terms.tsx +++ b/src/views/main/terms.tsx @@ -5,8 +5,8 @@ import { SiteHeader, SiteFooter } from "./chrome"; export function TermsPage() { return (
    @@ -16,7 +16,7 @@ export function TermsPage() {

    Your content

    You own your content — posts are your signed Nostr events. By - publishing here you grant Nostrbook the right to mirror, render, + publishing here you grant nbread.lol the right to mirror, render, and distribute them; that is the service.

    @@ -48,7 +48,7 @@ export function TermsPage() {

    Contact

    git@sovit.xyz or the{" "} - GitHub repo. + GitHub repo.

    diff --git a/src/views/tenant/home.tsx b/src/views/tenant/home.tsx index 453f34a..34dde32 100644 --- a/src/views/tenant/home.tsx +++ b/src/views/tenant/home.tsx @@ -25,7 +25,7 @@ export function BlogHome(props: { return (

    Published with{" "} - Nostrbook + nbread.lol

    diff --git a/src/views/tenant/not-found.tsx b/src/views/tenant/not-found.tsx index 1e2ee90..9a63fe2 100644 --- a/src/views/tenant/not-found.tsx +++ b/src/views/tenant/not-found.tsx @@ -3,7 +3,7 @@ import { Layout } from "../layout"; /** 404 page for blog subdomains (unknown slug / unknown path). */ export function NotFoundPage(props: { handle?: string }) { return ( - +

    404

    diff --git a/src/views/tenant/xml.ts b/src/views/tenant/xml.ts index 71fdef0..cc24d29 100644 --- a/src/views/tenant/xml.ts +++ b/src/views/tenant/xml.ts @@ -35,7 +35,7 @@ type FeedOpts = { title: string; /** Short blog description (profile about or a default). */ description: string; - /** Absolute blog origin, no trailing slash: https://alice.nostrbook.net */ + /** Absolute blog origin, no trailing slash: https://alice.nbread.lol */ baseUrl: string; /** Author handle for Atom . */ handle: string; diff --git a/test/fixtures/events.json b/test/fixtures/events.json index 35a092e..c361083 100644 --- a/test/fixtures/events.json +++ b/test/fixtures/events.json @@ -5,28 +5,28 @@ "kind": 0, "created_at": 1700000000, "tags": [], - "content": "{\"name\":\"alice-test\",\"about\":\"Nostrbook throwaway test profile (alice)\",\"picture\":\"https://example.com/alice.png\",\"nip05\":\"alice@nostrbook.net\"}", + "content": "{\"name\":\"alice-test\",\"about\":\"nbread.lol throwaway test profile (alice)\",\"picture\":\"https://example.com/alice.png\",\"nip05\":\"alice@nbread.lol\"}", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "id": "8eec4b7f06d1524da31a4f8efa7af9b8f5e713f26a1cedb4378df5021b974296", - "sig": "642c174b8d4dccce09926d6408f7d817ff5e35a693aded43741475b36b1466912cc877f8782eb39fd65e0b4638920d48cbc4e8b1713a57d23612b88963cab58f" + "id": "c1842f57bf7d89039fa4afccd5cb43525780665692d7bc8de56d80c2f89ae233", + "sig": "895ded1de777f118d4aff5f2fe676903b9cd00f28451439648f4d6d6e2ffe042add4a3ab570ac54286657f9a747be44701818fb04d47800faca5980bdb3fb21c" }, "bob": { "kind": 0, "created_at": 1700000001, "tags": [], - "content": "{\"name\":\"bob-test\",\"about\":\"Nostrbook throwaway test profile (bob)\"}", + "content": "{\"name\":\"bob-test\",\"about\":\"nbread.lol throwaway test profile (bob)\"}", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "id": "a18ac9b38b24ac3d671041a7aaa5efa392e11e79f209b2515f63f4c3a9f4572a", - "sig": "11fb8ed3641afe279a6b4f86e9348f190f2d1a69ec286fbf6bb84c687db6584c210053478ae051caa9e5832f8cf00980172ad12f7a81975333c0fadef2775466" + "id": "f6f2b9e6fba9e9c9486dcdd885502fd9cd1507eb36da333f895fee115cc4d568", + "sig": "b6003778495d8656cc0e7088aec6d2d84ac6079582d3f4bf96f819deec83000e778e777aa039b9a910c5a70d8f5927a096bafeace5862a4f9cce239112852fdc" }, "mallory": { "kind": 0, "created_at": 1700000002, "tags": [], - "content": "{\"name\":\"mallory-test\",\"about\":\"Nostrbook throwaway test profile (mallory)\"}", + "content": "{\"name\":\"mallory-test\",\"about\":\"nbread.lol throwaway test profile (mallory)\"}", "pubkey": "531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337", - "id": "064de8027670af9ec22d007945463a8475508f6a8e20b7c223012b6c591ecd59", - "sig": "2d848b7563aa545b984874cc8a1622b5e14486506988542570b661861bcf9fa5f96672299e88802a9fe4245ccecbcd7366c8ce36f6373d7a69522587563dd4af" + "id": "c77f7e64a6430e0d8b767847770cb684f5cfd883b563926b425f7825dc802abe", + "sig": "0358bc44dbba1939fc908fe75a717764b3bf8920d981a810bc8e32674ea0692d6d8942d651e0796208d155b1579f7d2927ab5f0d5d2ed73d01d7c54ed73c4b40" } }, "posts": { @@ -44,7 +44,7 @@ ], [ "summary", - "Alice's first Nostrbook test post" + "Alice's first nbread.lol test post" ], [ "published_at", @@ -53,8 +53,8 @@ ], "content": "# Hello world\n\nThis is **alice**'s first post.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "id": "1a9ef505e58fd74986ee324ffd74bab15fc9d37244613b42b7f00fc49d6c4330", - "sig": "6ee053b9132287b978a6ca833a9e4cbff1e896e123cd452cead0f5609a84c7e336d5575eb599cf3714de1680d75eb82021216381774d31c6b3d523df696f79f6" + "id": "6d915700436c2f0203f9c5cc93ae8669a344bd01cb5505b0ac41e15370b31e69", + "sig": "0ea768b79b261d208a39ab674e0e34fe05864dcab184d699361625ffe702a8f3dcb3d5010adc0e1f2a4080af7811ea99a276ac7ef65ac1ee77cd53e2a9d028e4" }, "aliceTorture": { "kind": 30023, @@ -80,7 +80,7 @@ "content": "# Heading 1\n\n## Heading 2 with `inline code`\n\nParagraph with **bold**, *italic*, ~~strike~~, ==mark==, H~2~O and x^2^.\n\n> Blockquote line one\n> line two\n\n- bullet one\n- bullet two\n - nested\n1. ordered\n2. list\n\n- [ ] task open\n- [x] task done\n\n```js\nconst x = { a: 1 };\nconsole.log(x);\n```\n\n| col a | col b |\n| ----- | ----- |\n| 1 | 2 |\n\n[link](https://example.com) and ![image](https://example.com/img.png)\n\nFootnote reference[^1].\n\n[^1]: The footnote body.\n\n---\n\nFinal paragraph with a bare url https://example.com/bare and an emoji 🦩.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "a4fcd67d08787f86e34699566618beca37a1e91fa74a90a27da374331641bf97", - "sig": "383bbd91ee65fdfcc590bb3302017b882b01ab332c2de059a4a88e9495dad13656a5ca4ed2198ad50df2caaa927da58acbc6143d03d9ec73844d77871ab5b99c" + "sig": "9f9deb284f87745ce3bcb13670df242d0546056252c4fda5a0db22737c60f3af057998226f1f2299aa05368301740e972b9803d30e8810768c1d12168aa388ff" }, "aliceXss": { "kind": 30023, @@ -106,7 +106,7 @@ "content": "# XSS torture post\n\n\n\n\n\n[click me](javascript:alert('xss-3'))\n\nanchor\n\n\n\n\n\n\n\n\n\n

    div handler
    \n\n![img](x\"onerror=\"alert('xss-8'))\n\nalert('xss-9')\">data url\n\n```html\n\n```\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "50cd8793666623c4e095578e79ed1b03b43aad1ff15e42b014f474c5ba3e4725", - "sig": "b240f259ff29f941b675c4dd86f5a9e4a4ffe966d9f8a04a9c443516375a1d7ee121ba81d561ace12e854b8a26057afc8bb0ae9ea289cf488193ce9d7a1287f2" + "sig": "eaa27520ac58d8acc64eb89a11eec935197417da2c9862f7e1c138a3d14b9f31e13d172f0f317e391f2ba01962dfbe2551824ebe1283beec8c45009bbcc49609" }, "bobFirst": { "kind": 30023, @@ -132,7 +132,7 @@ "content": "Bob writes about *relays*.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "107e02ff45c444b3c1f17801cd1e2cf2195c1f74981661cbc9bc577e7be2460f", - "sig": "2aec210c8509f2909cd121e5d6b4179dce1c5c459b2a36e5328c1d28f09c570d79fbc124fd74a6fc3afe9c5295771dda6a1f3c3e38ac56a808eede095f0a5cd3" + "sig": "c8774b63cbbeed2703c17bb5e151cb10c33e8948486041a2fc5a0ac11b62f693b09053eb9038927d77024fc7572c9dc2565f6b4b08df741836f2f5d4a14b3c3c" }, "aliceEscapes": { "kind": 30023, @@ -158,7 +158,7 @@ "content": "# Escaping torture\n\nQuote \" backslash \\ slash / tab \t backspace \b formfeed \f carriage-return \r end-of-line.\n\nRaw controls: SOH \u0001 and US \u001f between words.\n\nLine separator 
 and paragraph separator 
 inline.\n\nNon-BMP: crab 🦀, clef 𝄞, family 👨‍👩‍👧‍👦, flag 🏴󠁧󠁢󠁳󠁣󠁴󠁿.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "aa72e0c89dd2a7f2d7e586630069093aba4ce4bfab14343d9c9956a40e5efe10", - "sig": "54c3ac7ac94e09b7e8826dc1e0613baaf1ab5ad9594273d82a89f9d607946cd848cbe6d040913f3697758e58b5260f50d412a15cb21092ac0c066b21a821b9a3" + "sig": "d5620026bb4dc04dfb5031078ef24a1db0681820f652d52ed66c512bce795c51107821372b3b534531b31f435b784b8408e80be5b0b9718281d856489ff8f5c4" } }, "replaceable": { @@ -182,7 +182,7 @@ "content": "Version 1 — should LOSE to the newer event.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "12e6db93b8a6c0e967902ad6b35a2862554fd2986b4aa9175dbbae338e25c397", - "sig": "000a8304d666037236bcd70da009574bcfd36f709fcab8c5d42e76e6d3540d938f09352051298f6f3d56a1d583dc81dd4510763ff36c2adc375a73fdb1919eab" + "sig": "b515525282051bde4f58bb04ff4c33033caf3f3e1424893676e3131a8cd070f89c552d3006e5fa7763861bcfa9a7786bda33b167180a49b7490a39eb8c8ec51f" }, "newer": { "kind": 30023, @@ -204,7 +204,7 @@ "content": "Version 2 — should WIN over the stale event.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "638070b0698a84d8d13d6a0ec30044d3d4f8f2e7a1341fea59cbc3e836b08352", - "sig": "0594f955eabe27933347a705d5cf96a116582cc90f575a604492bc25617ae643eef520048143f26b867771d23af3bc29a32561ab25e5bcb63ad345cd37910122" + "sig": "ac1333aa223d3285714b82525bb437fa61e7d280dfdbf85a307606b86785f5c6b969d428ed211d3ae51e076ffeb980d7d6aa9ffe595ac2147ef1bc217045c607" } }, "delete": { @@ -213,7 +213,7 @@ "tags": [ [ "e", - "1a9ef505e58fd74986ee324ffd74bab15fc9d37244613b42b7f00fc49d6c4330" + "6d915700436c2f0203f9c5cc93ae8669a344bd01cb5505b0ac41e15370b31e69" ], [ "a", @@ -222,8 +222,8 @@ ], "content": "delete test", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "id": "6c1135b2bb95240ea399b9063935ca489d8fedec022594bbfe296da96a1e19fd", - "sig": "6720ba040e0c2822ec5fb053bc4747cc7c73a97e59ac30f26e361e61ec17c6ea22ba19cbf1d64fe0d3e44ea2dbdee9e8829f042c5e5743c3e2111edbd8d114d0" + "id": "45e144cca9493c1756b4cd6ecde8344c7f42cc7a2e9fe1d7bbe5ba95bbcb52ae", + "sig": "7e0e5e1c16ab0f9c99d83d6b744c1385f13799739555a7ed1215f065538746f911518f29e64f44bbc00784fef032c43a495d91576f0e6ae07016f1dfcb33587d" }, "extras": { "deleteByMallory": { @@ -232,7 +232,7 @@ "tags": [ [ "e", - "1a9ef505e58fd74986ee324ffd74bab15fc9d37244613b42b7f00fc49d6c4330" + "6d915700436c2f0203f9c5cc93ae8669a344bd01cb5505b0ac41e15370b31e69" ], [ "a", @@ -241,8 +241,8 @@ ], "content": "hostile cross-pubkey delete attempt", "pubkey": "531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337", - "id": "f2472d7dbe36e93fee22efe0627a37e93608da51bf5c25d03125a98ef618f586", - "sig": "5208f51aee33aa486a02565b4d4b075d1a0ad52a6713e9662fa1eed4ac1bd5e4a24a133caaa4e6d93fbc9b5275af2eca8f1738ecd12dd1b1604065cf8b362c75" + "id": "f347689b1d839c236c158a2b63a92b92f7bbc2da9c85342514123c4956123f6b", + "sig": "35585fe772f1995a119e4320cbccd74ef59ced35dde55d014b86a71a68749850464c3a94cf5c75224d430bf28945f1addea1cded6db5af6eb13df67bd54e606e" }, "aliceProfileOld": { "kind": 0, @@ -251,7 +251,7 @@ "content": "{\"name\":\"alice-old\",\"about\":\"stale profile — must lose to the newer kind 0\"}", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "232338890ac88a3651c600edf9850c7fb0275898b28dd59dc03c533d9315a911", - "sig": "44e6ff1d55f054393abeb394316755979c6e166cfb56305c60a51209ffcaca4381b253cb5c5ded72ef27e559bc550beeac994047cc1ec3c17cf1561f76da5b68" + "sig": "da68be37ccacc0df78894a01af04431d7abbe958ea66ffe77923a3341357381da863b27f6f0b02850ce1f0ea521442f9e25999fa4d0c8de5a8057720c8e8bfd2" }, "aliceHelloEdit": { "kind": 30023, @@ -273,7 +273,7 @@ "content": "# Hello world\n\nEdited before the delete, delivered after.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "65f5d55acbd9f87eb626d51a469dfcef9527a19bb61a50822d85c8073b8e9b33", - "sig": "18a288742b6244e404f2bcfa862a8674638f0ba86efaabc9d8bd245a11d6ff950910819c9a5aa4d9e65b9eec9022b7440952fa6e43fc56c41461725025cd1de0" + "sig": "04bd01a28e7c6f6f3795a7adbeb1cc601b2924e396303b73441845dcaf892b2de0a770ef921185e1134b3d8a239b875ac42eede71e50f5496de199c0146e874d" }, "aliceProfileDTag": { "kind": 0, @@ -287,7 +287,7 @@ "content": "{\"name\":\"alice-dtag\",\"about\":\"kind 0 with a stray d tag — slot must still be (pubkey, 0, '')\"}", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d6f11ba16c31aef4f0e9865a83d3cf1576fa8b32966defd15523bf2760f700d1", - "sig": "9e7176b4a401abbc9f750a26756150b1f305554806e01934d280c65cb69f9f9a5abd9035e5f93ed35da7636aecf679668cd648619dee793aeac8ce8eea826374" + "sig": "912962a78de594605e0aa8789fe431cb47ea81226ea1876a0641a39b3d54c2e74831b573531964bceffe228f84ce62f503fd0018482d482ace670d2b1d03d072" }, "aliceFuture": { "kind": 30023, @@ -305,7 +305,7 @@ "content": "This timestamp is decades ahead of any honest clock.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "dbbbd8745ceb2efa916ec591a810615da41c462a4b60aef48a857a2a035f639d", - "sig": "6272e498577fc58d85479e91f9dda446f606bb267fc7eb1cffea58cbfa44c71c4254d31f2fea77a083e9d8c52777ce58ceb6e76c5982c7e744992ad2bdf99ccb" + "sig": "d340a3a98eba6b05e1c6bd197701932b7475a758d51f63d1525250d24f546fdccfd0c40b585b5c11faa6f8fba8f687011eebf2cb4ba62b570810ea1d351f9aac" }, "tie": { "a": { @@ -324,7 +324,7 @@ "content": "Tie candidate A body.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "448fda17cb2f3f10d24117b92d5051dfcba7dcf298352bb2d351b395e4897a36", - "sig": "c4d4504ef46b74b5922d0e2cf9d6a6810c5ee77a21949ae0734036de8f837ca1cb2d81e76af6e3096e7e1a0c16c85b16110c0fbc7b9403bc08e15c3850e6daec" + "sig": "a47cbc9edf55ad2d708a82b3c145e3a7ebce1add1caae0ecdbd9e2a61c71853bce430139a07d4220a1b465fc3f8e4e23e3b9d59d420227ca9369ed2c0f8a01cd" }, "b": { "kind": 30023, @@ -342,7 +342,7 @@ "content": "Tie candidate B body.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5575bcd466bfaaad66cbbf56a7e3bd05f23eb6b9c14d71570f70e00a88c3a168", - "sig": "6a220732e51068f4918a871d1e569d42b4a7faeb2caa16a8c44355f7015d2d22dd638bc0c8c3b3dc36b91a201f07b9a3966ce85a43ed11130b492afdc9152f6d" + "sig": "046fa982fa2deb8e6e947112b38f249aa905e766329000dbe8fd7b0ecde2d71fb05a70fda5c2102cd06f2814f7e6d8950ac6fbbfb3d620a5d82208dde0f723df" } }, "bulkBob": [ @@ -366,7 +366,7 @@ "content": "Bulk body 01.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "27a2e9c5cb7a4f2c18b4f075f4eb135f94e47c689fd1692d32e4e12feeb4265a", - "sig": "629451aac0dc088b1b7b4e2260952948b238597a711ea3ef9a39be2a6b01b17439062c98c3a4e50b2ffa25922deb9cf0212d0377bcae90f4e5df64d8950ce12a" + "sig": "c31eba9a1a669553e90a38091b6964c871af217954e1b4da8f280b86e420b110c4e6648723cdd6ffcb99c5890056f8ae63ecf73d340d598efa05230472328d9b" }, { "kind": 30023, @@ -388,7 +388,7 @@ "content": "Bulk body 02.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "b88d89645d4eb733ef78ff4b3a7b874948eb3cfb1f85497c084b5f394491d429", - "sig": "9dfbac13488eec239b05e39daa64649f8f57c229e81ec423b7942a3e6473d9133befe48e70770edfd59803f4878b312873a3df28b56f33968c0c5c6b9660edf7" + "sig": "43f88537a5d1f8bb0eac390a25f36a3d081806ab8679125a3fdf603796545c13f7629826a5fa283d91ded8125bde3fcd2e057c0db590d7ff8303c199c34a0b29" }, { "kind": 30023, @@ -410,7 +410,7 @@ "content": "Bulk body 03.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "746b524ff15c5ea16323ac92c371586a191c5ce22a69a2db9bc1735915f767f7", - "sig": "a2a6269e09b76355e663f262a3c9dbf06c3bd5a871067d5a3af570cf81c6cbf47c0c8c74f12111743fe0dc45b16759d5577f9caea4666045b0804fbab24b5fc1" + "sig": "7a673a28c76b0e087172f96cd4b13b588c922e78de5fcffe555935d2769ea8121adbc8c363db8b2c9acd1dbb452e82a3c3c36663c9e2786eba60e46738d1db66" }, { "kind": 30023, @@ -432,7 +432,7 @@ "content": "Bulk body 04.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "f6cc16260d523d9ea092d7a658d3320d738fa6cd5967803c821b72c2cd697c5d", - "sig": "3edc132ebe30742917c1925238d6361dea63f67d21e7802b44dea874bbb293c5fcd0e596aad9e18dfea94a175b1e61fdd4b78386add5782dfa201a5f37cea346" + "sig": "c2d2547a484f70acc87af96d484dab5ca79c28cea550fdd23d53d90f7f17deb078ea367e9f3ade2766272e98bca6031ff7f8053e07aefcd2a01fd4b76b81fb1b" }, { "kind": 30023, @@ -454,7 +454,7 @@ "content": "Bulk body 05.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "9f8b3f7b154393508516e47e60fe2fd4b05fc0d9a4122f1ee0f96d29216ec250", - "sig": "3ed8b4fa0bccb984b78d6926bec5065d230824b91ab1cade9fe41825b9ab341ad8ab7fb4eaec6fb78c29e899af869ddfdbeedb6154185f4e101eeebb9d733307" + "sig": "a1501f9a8e2cce3d0f6c8ab1201f5cbff3a38d273b177272b85d8cb619ba4a8663b651f9f0fb657805b5d2f9df6dfae9e7febcf6e30ba972952b20ffadf44081" }, { "kind": 30023, @@ -476,7 +476,7 @@ "content": "Bulk body 06.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "274325a9ba3100d26688f7a65e9450526bf15ca00482ce6b82353cd386aa29e6", - "sig": "46dbb041af87f5c39830123cc96edcd085a0e23cf279962dd0f493141a9b13269f51a222c34b6b2e4eb1efcf7beca7db9e4a5848f767d7d60cccdda2e62af822" + "sig": "d6254a0a3e84e4ca6969dfa4c2a079fe88f212cbab2a37eec002c1806c8f3b5552a3620cd3594d44a352ef395aaf7745d11671d700eefcf29ff76fb131863bf8" }, { "kind": 30023, @@ -498,7 +498,7 @@ "content": "Bulk body 07.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "4084c87e0c7e5646c829aa22d42dec1ff9654627adf15e266ffc0adc37489ea0", - "sig": "a39d4bf6644cdface93927d77ec78bbc25e3b18e9e1d9003922656ef3210addaf82f2598921549d63e2de1abde8858980d8e69801242f9385962bc33d4c7f667" + "sig": "2efd318a9a946e66e0c35dd6bdc20f46da7a68f9449218144c726b4ab6ef295fc22884f1d64e739b822ad93ce33c37f28157e191a573687ca8a425f02eed2f84" }, { "kind": 30023, @@ -520,7 +520,7 @@ "content": "Bulk body 08.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "ab21da39c032ed715e85c68caa10ee14f76995a33d99891b7cbede7a37cba521", - "sig": "fbb7be5d6cc549f4dbde055ccfb509660a072cbf8162aef04590ab521521c8fc596988042eb47dd4b8cfb1fb00877650e0bfbfa7beeeaeb7cce195b8eae57792" + "sig": "ac563a66160b69f1847377a98f9e39bec7c60dc911c185f8b2ea79cdd4a0bec40f99d94ea488d178f3df1fb06db8e24c9fc7d855eafa6ad73ffdb124ad8bd074" }, { "kind": 30023, @@ -542,7 +542,7 @@ "content": "Bulk body 09.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "49876d7c7cece55e739eeb060325b2bea5e3f70b597dbf3964f04959d2ca6762", - "sig": "2f48d930a18aaef310d294b330e60f167e30035b0d79ec2db5f43a040aefd436fdac75484e2cf99c2a2585cab34afd0360999d97b926f8da395b80da01b36855" + "sig": "b363f7f1b70dc546481cc8eeb4fa1461300affb5916a0f6f77a844eeeea2d8c8977230c3f57e479f9c58539b6d9a4b79da8ea6ab1bf0bee602efd8b9c14946b2" }, { "kind": 30023, @@ -564,7 +564,7 @@ "content": "Bulk body 10.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "015e41b1335d2b2f9e0b67362c58bb3bb90d488d00b7af8362e7c37537e22ff7", - "sig": "79a35d7ef0f07fc601e849e8c83d3a1ec45018b82c9d3dd59801e79eee616713a68821549418442dbe2afc1ed87daa0992c4d52a99240e76ee1d780406c3ed97" + "sig": "32fb5b35aac27a75c9c2c39718ba62528f6a0b9256cd760cf267933a5667db5a7eedbf38f7c365b714d60b2e68488210af902023d790072e6091c42a327def20" }, { "kind": 30023, @@ -586,7 +586,7 @@ "content": "Bulk body 11.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "cbf852a5c469ed13f876e00c604a09ec27ff49042b2a4b8be82aaf21c3f2c584", - "sig": "eba967f133cdfce0b1f568f30a15aeb223755abafacccf621a71d4f870ce309225a5dd4da735046d3ff2f7578f4bd459687e5120c21c41a161c89c9d4923c7bd" + "sig": "2b6ecf69c49c8d28357a545c2f616e4b505bf5d7a7de573f63f15337002c8fe67ebcce61afb139cd83933259d3f495408e0acf1040087a790c7c0ecb11242902" }, { "kind": 30023, @@ -608,7 +608,7 @@ "content": "Bulk body 12.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "id": "e8de22f04d8089f1c7805f5d1994aedd35bf76b1d98d1a9bc5813c3393395fe9", - "sig": "657576ebc5336d97d1a24e2da67ea068b42872a5580e647f134289421285adc1caf7767a90ae4b481d2ca241a99249ddd7a75b1e2db6939c3decd4cb55af4996" + "sig": "ead3a200be7e3404f3c06291c0f784ea2b120a3ee3d9fbc1b9f3239f73516af0990ac202ddf778ba175caf2afa5ccfeb6b3efc1297363dc19199858a71ebcf67" } ], "floodAlice": [ @@ -628,7 +628,7 @@ "content": "Flood body 01.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "6d6cc6295103dfe2751a0aa4fbb6c610fe3db3926734927a08d0750ae7902e7b", - "sig": "e8e523cafc8175c52232074719a94ae28762b6ba4e819db0bffec84614abeb083533ba6d13cd2a53845b62ddf38e148a2cb4665975b541d5c8197089c643b27b" + "sig": "151cd32b3514f2145c49ad99c11e617cf68a5a0d28663dec73ab2b1ae716ecfa81f675c4d101f5cda0f20d04c2e3200b1662be9232b6ffe6b38bee1905b40ee2" }, { "kind": 30023, @@ -646,7 +646,7 @@ "content": "Flood body 02.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "8307f7c80761ceb682a5fd84dc0f3f68e9eb49ba89b74ef689d7fa5671668006", - "sig": "420cab90aa3d0fa8ad1c0029207cf1c0e3781c70d39bc62cac3cdb28708677b87152fc38957c469366e8752f1ceee597a187050d50250c7423dff66d4c77f369" + "sig": "9da361e990e3c141bb4b72132f30e5cf1267ebed7285a5e364646d74cc6788fb1872c90877446537807eb7ee62b15ab22e9bced06a13e4acd8449dff9d6ca68e" }, { "kind": 30023, @@ -664,7 +664,7 @@ "content": "Flood body 03.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "cf8c31e9c607b0bb21e7b35e07b030636f2710a14bced4dbbe0aa5954b456c57", - "sig": "badd9ac598608f201fdef249b3914ee2ad6360d962c8e0b19084043a519e244c2edb88834f10085ccb46690b1d6bca4a0b371c20bcb1521b819dc5c722aa673d" + "sig": "59e35250ca958b2c5825107470abe0071eec7672203caa5037f6d3f96c91987725ebb704905842912983d8e4ecc341383bd522ece5b41a41fbe16426207dbb62" }, { "kind": 30023, @@ -682,7 +682,7 @@ "content": "Flood body 04.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "657aa72a2515f70514e4e4f128ea129adc9c63a7eed82068bcf0fefaf898fdd3", - "sig": "f9cc70b6a61a29678cfc7825e37eba149ab363bb5bd4326d31c1a8c205022e5433a6078a8b809f0839d5bffee50efd31b78ad08d38a22f8a96ea51650f3120da" + "sig": "c2f406f47bf5e7ebf778bc9c85a325da83f3304493393c744076e954e31b241efec7e0462da671a51b2e24d5471a36921fd8a5e0c51e5f013a31cd0ecd8f9fe9" }, { "kind": 30023, @@ -700,7 +700,7 @@ "content": "Flood body 05.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d92a0a017fee0f597281e4be2fc0457778e5cbc286b2b870f600bf6c12ee03d6", - "sig": "73c049f57daf85789a2931e7c927b24d7f6454a97b67874b375c1a267fe443f7da85e8dbc23840aa9b95369cf5743b42eb07a18be3133f389421bb2575721f5d" + "sig": "676752d279f515b70cfd50ec5373e0b72c527c177f1fb0a06a86e43537c13b4080fbe427d6481c1b409ce1b1f309eae49f9905212b2feaa9d5bcf7bbe60f4ddb" }, { "kind": 30023, @@ -718,7 +718,7 @@ "content": "Flood body 06.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "6d0eae47094cc71b9089669e15a2d8eb8b4d9faaaf82d14e1dc1af84f4878d9b", - "sig": "d949c18aef499260c9412ec5c960e701fbcd1ace94ced92d935d8443aded08997c2a1ff7e3352bca9f37980560aea0d1a839e058d300df18025b90caa007dec8" + "sig": "02d76f6d4c9fadd608a1dcf9726de53a71888fa58e8b6bf102069889dd3dc0c711a098e971d450061ebc05bc9c6312ff31ec5383806d91fbeb186de2ef67141e" }, { "kind": 30023, @@ -736,7 +736,7 @@ "content": "Flood body 07.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5838737ed8558558420f81aed41eeb2b35d1a9605b85c96907c842952fd7e5d7", - "sig": "f906a9b4e8ab9ca48fb1a6edae261807dca6daa4189218f462231e3d7527779654b9134486790011bd74d0e35c21b98ce055d53776ae447eabd51f9dee97981c" + "sig": "5e9c8639756984c896ac15bc7a0e1157013c3505ed96a2fc410b3e85e864a3dc24b7ec081f4d4816369be2c65bd545d42113cead3bc3aa123b18ffaa81997fa9" }, { "kind": 30023, @@ -754,7 +754,7 @@ "content": "Flood body 08.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d1cfab4453e299bc758e43261adfcc3db6bad654f4c97316a7470e916b768085", - "sig": "a3e23daa3e7442dc7fdbecd1e3b89a4a692dccc78172b9bcaece3ba0c8e4330af4c7ff0e99d554662885fd4b09a81b7c7be0bc5e4097b998bf5a99a58aad6d89" + "sig": "13368875d5abe6c4d7a2ba301e7a23e01379ee66d08507802d5b9834e005c049dcd81fe613251bd3885e68c6e8ce0c78d57cb6601894eb2e0a424db29482f2b8" }, { "kind": 30023, @@ -772,7 +772,7 @@ "content": "Flood body 09.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "00cd11c2b54d526c8b4a382c2bf6ac02988190a42e7d448622f18c85d5ae432e", - "sig": "654a4954ad9fd30a3663365d54311bb4e818f388f0af4b028c2c94c1774fdbe0a231329ce52b775ceb90bf785b8e7555fc4a16073c90f00713d5f88242b0feb8" + "sig": "5340c89bbb4d0f6c4fa16e8fd14c4dd59b359054bf785b28c37e90f290b1670caa8cd6be76fb7c2dbba43c9ddf6809465ad33a2866b33cd7eed99bf9f2647dbc" }, { "kind": 30023, @@ -790,7 +790,7 @@ "content": "Flood body 10.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "1acba86b4e1f578b3837d4ce7a229b50e43d3c840161c892bb4d68f1d94dc5c0", - "sig": "b1cbb4cb591acd70c011471c95f10ce39d3797871d439b71cfac9edd86d7c71f6b2bf587b2aa5485e68d0aef5c149b5cf4c045db4b608f9a09336cc76d3667f6" + "sig": "f24882be20a67be230a319e04aed23a2bd5a8aa20ac2c0688b7198359a1601eb8aa35d34c3abcffc5436ec886107c6f60df9d9724b89c67d74289f3e79a24ee0" }, { "kind": 30023, @@ -808,7 +808,7 @@ "content": "Flood body 11.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "c9b9458f559683d186bd631de823efbc1d6239351cadc90f6f84f60205a9b6b6", - "sig": "dbd8ede1114255e79168cf14f1fd417d79cff0cf13f25fca88575cf4af407c76c2e6e063443a593ad5149d5d317a81548e1ba9cf27ab4c4c2b7aa829200cb75a" + "sig": "7bb3debfc13074c91b281e50aa0893f2313aa4b7a7fdd7713ec138e16660ec86612fc5a25c71cfa8c910ef0d0cf9100f00687fa721702a1ae3d5e77be00764ae" }, { "kind": 30023, @@ -826,7 +826,7 @@ "content": "Flood body 12.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "4f30da7ea6e5f8d46bb84d6081989ed31bc17d3d9060170b9409bb8d844a1a6c", - "sig": "56db6c179fcb14fe0c476a8fc8c9dbc3b3977ebbc2a83b93607af3f6bec980b422a8e28b1ee10ef2bfcc06b4b0579142b82fccc237f07e1c33ad998a62ba1330" + "sig": "877f28dc8c11391284e22af208b8ad805b5aee50eb01c6eaa005259ace8e8062f2c41c0cfe9d9b897ea43d115f17ffcc68cbba2a9af7f91a7400532c487449ac" }, { "kind": 30023, @@ -844,7 +844,7 @@ "content": "Flood body 13.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "3db6fa4037550c38169813216c033c8d4c5423daaf9d1e78a7a93f71ce5939cd", - "sig": "b1715079e571268003fa1b578962fc1df3e47e9ec4933b77086ed5c09dc662104cf8b0a27e64674b7170e60476bb73b5de22516d1346a99482be8f5d374b7fd8" + "sig": "40123b6e62df53d3c9e2ab39e68177408789b21445871a1916fda2b9a8c81b620e97c3f5e37b57a40fe1a1bb825224232d91ba0e0ccd99c7f0d19b6b53905c52" }, { "kind": 30023, @@ -862,7 +862,7 @@ "content": "Flood body 14.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "8fe40fd036b2379b06a618bcc598ce3cf40d4afc301ac6f2b59eb6791e2e37e1", - "sig": "34b6a26cc5fe0524a9b2d46b490ec41d443f0f564df48bab82b047bbecbd01e46adc52cb303f2d7a5645b1e237b42ce5e0ed4cf7e008f218147bad51679abe0c" + "sig": "97b74d396d60f458a34fb750e185ccc665b0b0f87f8c26c24c50449cfefb584774d2482594d4549674b158f36b17b5511aaba2ad6731cc8e67ba86ab2e819bb8" }, { "kind": 30023, @@ -880,7 +880,7 @@ "content": "Flood body 15.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "0254237e876e8381b46d9f4f30fe6442490020127b10b18ab518e0a08203f4bf", - "sig": "c0b0325e9ba636e9a28b594c3e1ea8ff1e7cfd5ab9ba12fed0d2a03c8321c4c8f19f980af0392cc8164bef743acf3072e548b2d435bc72553f37d2abfcc7318f" + "sig": "17ca8c991de7e83589a7e721a778bc96e9a32cb046a7a8df557fb6d066c1cd2a80f896d297dd7e300ade47e42a09e258ee749251628bb6a7930ad9740b48e9ff" }, { "kind": 30023, @@ -898,7 +898,7 @@ "content": "Flood body 16.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5f28d979312175111919508778ac7b10418d3e7c134697b97d6638536757d84e", - "sig": "70da2cb755c392c2d082bcb4ea9c63aa26500d35d81ae7a21d45593f2cb3706e363b29cda8ab3126881f7a9e5161410d6e17b580ae75cd861ef304c01104c652" + "sig": "649c10cd94f365989eece72953c204249151eaba1ac3ed7a7ef0f1b0d93e4ec2c885a84b5143557df86968c4152f175f56e40029da862c69b72c180f26687474" }, { "kind": 30023, @@ -916,7 +916,7 @@ "content": "Flood body 17.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "63340bb47bdf8a3867faa34636747a3a0c465b680f5d0577c28e6fbeb9551b75", - "sig": "76e215f59522797d2d3c0277707dfd66fb657cdb96820f0829fc4cccda1029e30aaed2cf846b9547433cc22d119b43b57667e010595c89a1bed267df21fe781f" + "sig": "116f80186b78b729587dd1190c0fc812ca2de1bfef1991c9ec97f590f5ec9f286f9c7438771d24c9dc3dc018037b937baa0cb8d3bd369a64244ada7b66c54ce0" }, { "kind": 30023, @@ -934,7 +934,7 @@ "content": "Flood body 18.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "597ce6afbf908d9f6d23e79e90dfbfa052ce5b409daf006bf64a503f99d9bfa3", - "sig": "18524381e2be606d88762a1ac59ae648005cfb08cb5bdf7500f81c99ecbaeba9035085982046db3d74f6c04fa4d47b4562580681d52a107cd425cfd3f1cfb135" + "sig": "b8f08858b08db8052abd7fb46f911c3fa02e84837a7b9c3e11962094dca174c42f7e364dd1085a347a274b8f475ca670cecd4fedb6763150f2164ffb80657d42" }, { "kind": 30023, @@ -952,7 +952,7 @@ "content": "Flood body 19.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "38f69218f7baa849c0e0482c4199c5594e075f145da4036848aa5b0358018e60", - "sig": "670581b0ea9f68410282d9006b60dbafdaed9dd39fd9f97236655eb8677423cfc62910b5e5ffd20be62959d9fc1fec435fd994ba46f96710d0d8f8eb89332217" + "sig": "fafeaf381ffe8db081c7a11f6daf07c409ca4a21f849ddbae94f5a0ef5797d4310c4ec736f3ab88760371fea17746e4148eec1965cc17c366e4a887015cd7df9" }, { "kind": 30023, @@ -970,7 +970,7 @@ "content": "Flood body 20.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "b180cc023f38ef1547cee623f80364691eb122d20d513f7c4820fb34d5db6245", - "sig": "e1d720b76b44023ee1f08662dd48020588f3acb160f294ffb3bacfa4f3cf15294915d3245aea15bc962cc7b2a6a24a6724158d6f444b2466fe42909bf2bc7b2b" + "sig": "ccc3f372f784fa873152f7ab4f13a8dd03f19a729a1acf85daa862618f44e5804b7f49accfd070cb164efc8e0f5fb1cc08a23055e4fddcb94881786ddd526ff0" }, { "kind": 30023, @@ -988,7 +988,7 @@ "content": "Flood body 21.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "27c1a303a0e7957d6e735d2329a88595a41239ec5808da4c22755da48fd622dd", - "sig": "d1e815bd6d0c59f2b856b4db0a61a59c065d88de2447b51c2ba5b29926cb890473d76931560ca0c429498f18daf03b7a12d643ad7af4a002a674626f8d1fc795" + "sig": "9dc58861df0927bdcb23df0eef84888c98fbd2fef9abc937fc9f2d796d88e9d698f8e51d74797198f52865c012b92e46d1b397027b32ce4a99860f8ca8ca592c" }, { "kind": 30023, @@ -1006,7 +1006,7 @@ "content": "Flood body 22.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "25657c9600629b831e66889d5697a5ce42e8f4caf4ba5d0cd2a21b8aa0f61573", - "sig": "332004ccb51a004508c6dff85b6b6eeb55b06f4d01d12ce26068da861771c2f52fec5d4e90537e8202a2e2db36a41d2c1b9f7a8872befad4aa623dac1e010b16" + "sig": "9b51028412ac1c623571db33a8c7cb54aaa078c256726d95294b8922628ad468de5bece7716173aa63fe699ae616e048abbaa4c1145cb249f5ab95acb867e961" }, { "kind": 30023, @@ -1024,7 +1024,7 @@ "content": "Flood body 23.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "f7f7f29460efd3d06b4ebfdd1e9b34268a4ef0e4e37ce04f870e293621d6231c", - "sig": "d036942a1334e563992c650fb07cc6aab2c5a8f0b1fb378ab9804d96632de8209d3c8237eec9ad0daba3e22bf76ada9c6140b78d1c1e174a663f6f59c2876a26" + "sig": "4b1dadbdf503f2a29f08429fadca841adf577054ebb17e5347d2fd05ac23787c5a28cd68382e39f222cb2c343d4f0c6b11e135a9d559e3c4b1e51b3692043a84" }, { "kind": 30023, @@ -1042,7 +1042,7 @@ "content": "Flood body 24.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "4a43d1c52dba8ff44b066dba536bd1212a25b0cc88528b75624acbf9fa0f511d", - "sig": "0d2e9a5797b4c6308c1148dbc0010e7ac300d15521cdce44c1ce7f29226134235857407f50d5a3417d1ca626e95117480c030a960ea7baa68b050afeabf2d846" + "sig": "52af5d6c9d475a5d3795e16f5dda88a69b0d7aabb91fdbb7ab8da4bb2557c590183b60ba06bc45b50fa428314fda15e8d08c3d6c14b87dadf1201f1eee88d1e7" }, { "kind": 30023, @@ -1060,7 +1060,7 @@ "content": "Flood body 25.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "87a6141e1f453e6041c9500d657c5ce90003c236e5200b90fc923bb63c62032a", - "sig": "bf64c0e25995c966af7b38eb2151f79e34b0515210efdbc83d17656dd91734a64e25f539416d8e8aa33a2403792f045fc71cc2e0726769110c7e826e23062f86" + "sig": "2871f2e5ce746d2c6e73299a5fedf30f559390552b56781fca175c0b70bf2d872268f40d5b60514b236fa20d7731ee7eb1ebf503a28ba19c9bcfc751c1f9f276" }, { "kind": 30023, @@ -1078,7 +1078,7 @@ "content": "Flood body 26.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5a6cccbe84dadfe70c5a039b527c45b753c6faa3c7d007a3d4b8341e99f60b1f", - "sig": "cac436fa92aa8481ff79d09e4e1cb897575cb4692522e88dfa86cb8f0bcdb5c53210719ee35b461bf556b20c203e3bff44b419d8415d1fbbe3d4ea3891870ab1" + "sig": "87819a1b0dbc82a1ae119c9b954a928ae2d6d84bb365dbb87d51b94c565b718716ea381cd8190cef4e8d25834f18a1d5ae6fbe9e8340a982418ba5c20cebfd4d" }, { "kind": 30023, @@ -1096,7 +1096,7 @@ "content": "Flood body 27.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "0713af12629c8885c1faee599bedc7f15fc98ade03556503acc5df1d2b0b1b65", - "sig": "9e25342ba812edf32d5a70710fa20eaf0fac0771d7d5de6e2800ea8e417ec303dd1eabcd2fc9397d872332851950eea66ba67513d94102568f7a12cc01abaf24" + "sig": "e125cbd919e54c02ca6ab13da7a2f09ca15ca0a219ea76d5b25b3a103180f7908695ea2d60fd17518f45bebe223f05d93bc2f9cd489413460364007a78b872c7" }, { "kind": 30023, @@ -1114,7 +1114,7 @@ "content": "Flood body 28.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "ca99495e25c08b7722e252282bded3fac5dfa94b68ec127fefc89f9ef80dbfca", - "sig": "a9fd4a55fbadc9619d0ce5aad8f0a7b5df92d40a85beeda9a1629f206f7a3a09316695ad1bb09057385904544d756ca1bc6048ff92bb6c4d0f6454b4ac91e237" + "sig": "45dc0470c79174c262bdb251803c1513962eeda9771c7d7f555a8cc6d17bfb1528deed25b698ed96e844135d1a67b9fa9001d1cd9c3ba5d4929d38c5531f6ced" }, { "kind": 30023, @@ -1132,7 +1132,7 @@ "content": "Flood body 29.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "2b8326623456a2188bde6c4c577a36d297f46ad54d9cbcea4e6a414c6de0fe91", - "sig": "68aa9e4b9ce91a33d5ddd04ee48d62c5f13cf123d6ca29b80572f2e35cc49db5ab891876dee241f65bf3cd9908cd1fc9af5c52d38ad589aa0181b7f1e3d776fd" + "sig": "6f8415adb15ed9fdcb33964c06952b0aea56a3226b118849c2b94047c07f258795d2c90a4d96d72ec8c90697602fcee1f5bc005c2b0a8d500ed2b240ed807938" }, { "kind": 30023, @@ -1150,7 +1150,7 @@ "content": "Flood body 30.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "f6ee88d4ba563207ff47bf9336ab87e669f11566f037d06bed417aa77c94550e", - "sig": "b2ced81b9336ce29fd027e2aa0ae6955e0a95f00a0159110359598d0d5568161966fc016a91e48f2acca647105db83e38d2b5754435ec35b044c3f73d080cbf0" + "sig": "a1bc4f34e161330e71a730030b7871b13aa0e118ce05ea2900429121d08b261291dd7946c9d02000326750e8491192ea2cad7c73bf17188a46236761d9745482" }, { "kind": 30023, @@ -1168,7 +1168,7 @@ "content": "Flood body 31.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "ec022dbd7f9ed3371cd8349671c61cd886ae76e5d9948552f2b59b86880f4ce0", - "sig": "4c0eaf9d63fc026e7d0c50b9863830bb13aa74edce1e35dbe6e5a33c219c4ba83d016361cb2d540a40b11417b52d757f749083a093ad43ddc3ae6e6cde5a15d7" + "sig": "7bf837cafbcb6c480710a5952a5333e8a146e41d9284467abdb2fb3f0f80d426adac0378ed1736ce0f5859214cae9a25260bc4199011c0ed2c048750a23f08fa" }, { "kind": 30023, @@ -1186,7 +1186,7 @@ "content": "Flood body 32.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "1b2e268c09b04471ad4290d814a8d90543e569baef4c93ce0db2be560ecbbdc1", - "sig": "58141cf4f5346f3a1c518ea2febb0ebfdc0861a392435395fc1be89c6c6509a3133e81e32f773226f570a229b5044b58105a97fec771501e35c5426b3e3ebe48" + "sig": "b8956a74eebf247dd117b4925ca69cd2a34adbd18bc6743799e050710216d994abb87ac42f0108c6bc5946186e031ecea76eacc2ae7ced3cc021b29eac3c9550" }, { "kind": 30023, @@ -1204,7 +1204,7 @@ "content": "Flood body 33.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "cc03ddcd2bb890f484c55ea92055e5f69b858b425ba724237ab4c26a22de989b", - "sig": "33896a7d3fad8b1511bc05c094c7e6e3d558e998b257579da3e14840df9e2d66253fafdef13716f6cc11957e2bb53f5d9bf7a4315cdcb646e69e2185baf28089" + "sig": "a85b2ceaf04eec955d1439c83b2d218be0e70533863afb0ebb845f2812d5080373df27f5f99bafdde6664a02b06ff47d6e9513a57356264e448220c15495cd77" }, { "kind": 30023, @@ -1222,7 +1222,7 @@ "content": "Flood body 34.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d2ee0fdacaab4d4a387830c28f6fd6781343fcbd56c90df283a637cb31214d85", - "sig": "2b4342eee6b560c9ac84f69ab812059c4d9b20ae01c9fafefa52019f682215d7ec7602e151b8e807332b677d9229bae2f7b329a0d3bdd66b91ba1221e09f2337" + "sig": "a6fcccb6f9520fded57edc99b0847edcfadfd93e974465c96ee6acafa8b7994bbbbabd61a5000f43619af43b64324d1bab333df275ac46cffce633a08edf89a6" }, { "kind": 30023, @@ -1240,7 +1240,7 @@ "content": "Flood body 35.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "9213df583d830c1d07cf0643b8abbedbfd0e62812e72b569f04d0fe3a6037538", - "sig": "696ef9b62aeff90359e0760afaa67faa8656c63418e67427ba6345d32c08434c65c80ad398e4a167fb939da464ed70121d02c55d3c912a6f42e47016d242d11c" + "sig": "ffa4cd901fc55eb8cf0f40fe64ac57d7b291b56b302c1b63d2e4a37cf676f33c0917f1f0dcdc596f3f9079101b4f34eee0536740a6b144c706511d07d2e2af14" }, { "kind": 30023, @@ -1258,7 +1258,7 @@ "content": "Flood body 36.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "28de6539f3d6bbd0a09c7383dc57b6e347b51a2be5aeda2b736f10d5569ca9a9", - "sig": "2a20ba836352fb27e545ffd0ab789db2a9b9ad14e70099a2e57211a27c2a3c8d95b9714e146b7a9e4cd5922e2616da6c51fb48a0eab629516771e66b22d6f031" + "sig": "48bef27d662bf21296a5d31b83b8fc687d549f07002b62ad3e548bde109a55fb2daf75bf9a7a2f71babd4493519fb1d89db1ffacd33537985785971d56fa6356" }, { "kind": 30023, @@ -1276,7 +1276,7 @@ "content": "Flood body 37.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "e1a1a7ba881934d6671b94454379cb9bd9f9e103aa4797bb721e34b72921f409", - "sig": "27987d72a25bed2101334eb4c2dfb8876ae5cce9feec3e998edc8dc46776148b9190cb375390dffa09b4131e46f9bc9cf82d717fed8ea7bbe74f270495cc1913" + "sig": "65be1a0cbdc556c484476de14d51defd6ba1f62c3ad9454dd682b967e8a1bff3c483e16d1c160c529b8857049ac3e52dcdef319dc9e8986d50b88d72d1f43721" }, { "kind": 30023, @@ -1294,7 +1294,7 @@ "content": "Flood body 38.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "7b451b28b0d9e9d9d154a7591a4786a71fdbdd291d046e30206d687871c7bc9a", - "sig": "04cfa7b2ad360b318501226f914ac497515fa1d24bdc34a0285d17190ce0058775487f280cec61072e61b6d7b8a99ff2b5d3a4ab469399eeb87ebefb766f0ed8" + "sig": "0f44519d32225d256eac49ea8213da97ede5baafdfcef7465e80a11e3990f13c6dbc4f46be6881185c9697d27968f24eb5ee6c558b32b2e945dc06de718f45d7" }, { "kind": 30023, @@ -1312,7 +1312,7 @@ "content": "Flood body 39.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "738aaf3973665bf0cf29beb74be57a9e090d116cbf956f4ea5da1e0e6ab5a018", - "sig": "3a168baf782dd3319a7944dab1743675aa0b69f037e55ad6363d2eb1cc194bd54bf9bd5b1a78ae18316e862b44b6bc37bece0a7303dd763489c214e1967e0594" + "sig": "4821a317fe31ee81e2d5777d92ad4453a6878c2d587b522c55da891473aa02a21b11094fee799a5de62a3b5a2d2c388eed7a61dd4732436383cc98009f80a129" }, { "kind": 30023, @@ -1330,7 +1330,7 @@ "content": "Flood body 40.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "dc8819a50b1cccc02dfc3709221ba324b60818bdb5818e4e17c75bfd4242a048", - "sig": "08f3a6eb9afdd6f2b5d9bf7ae408d923eb29de4d1b9a431d61751e9bdcdf688567538ce55a3df2b29267c7ea1657cd2bda43b9627980d44ba3788d5d811e18ad" + "sig": "1fd86ea8747a8794a935e72d4ea6422a9e3a3003d1b952ee7ffa17d6806cc7038c86411a8cf69c56a0777e848bc9dede86e52dfdd0d65c9848c86e09c3d5e652" }, { "kind": 30023, @@ -1348,7 +1348,7 @@ "content": "Flood body 41.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "9e6357bf45ae1fd58efb5c3ffc2dc3a497e0ec5ebf7b9e2ef13b5a7aeb467f45", - "sig": "a290df16342f4091eb474ad6ffe694e1ceba2715304132616d00429a7343840c20292446732f45215a7749b97336c8f47aabe461ff84fadc52b21e057bd601a9" + "sig": "bae790f3f1d989eeccd36748b5ad16906d83de3418b85161b26dbeca524dab1335900509f17e00dfcc739f637eea02e703cc9f82317affc533df1026b35e80d6" }, { "kind": 30023, @@ -1366,7 +1366,7 @@ "content": "Flood body 42.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "27ee2ea1dbed6313422a5693a79d8973458ba64c9269e0fbb94688e7a039328e", - "sig": "56c481e65fb50cb772e6e0c6ce4c45116c801143c6b7fd39baf968d5212ca3347360d6a04a1fd311e6b0a33f4c33d83c6327e596d1d6a41180bd259c3dda4aa8" + "sig": "a946e3f085fe22e6e417b6e77c232e0041f08eeb2f770a9f42d45c63a942f602618186778066251403d1ddf693bb85b4169ec8d088f16d870b35804ffc031818" }, { "kind": 30023, @@ -1384,7 +1384,7 @@ "content": "Flood body 43.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "fe36be95a8120bc413e6a52bda1b5d6be9e0086f70c4187136fbb4a10257379e", - "sig": "ec171c68dba4693132c6365ac6248f18c810700753743bf22aa79137b8f8988235d2f5cc24c09775fdc88ef4ec5bfed2093669f0a8ba619ad5f6edc96bbda684" + "sig": "7dc5cac9fdd94a9c3f918eb23d7b7e3253b05ced93f2993945ab1c9e3a6b4d1211ce1bc260a1859ad1a998b1357c72f873b640e15f997951ce7fb9b250f9b8fa" }, { "kind": 30023, @@ -1402,7 +1402,7 @@ "content": "Flood body 44.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "254dd5b163e43bf75ea91df888208817fc1b18a0728c8d10e97791e8a9bfb3f2", - "sig": "d976702c550517341ce76aa47f73fff50f9b50f92a837b672ade0d042f0218a0bd0477c38ba570ca3213c1a0d9ac410e5e5a6008ddb209beeb0b56eb5645d49f" + "sig": "95408061f7d17797378c51e7be5c4877418dd2df4f3673b0d5899470ff5a40ea061dc4766e99bed034ea9702e969cc2899bbea84d652ee1f82f52602c1100897" }, { "kind": 30023, @@ -1420,7 +1420,7 @@ "content": "Flood body 45.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "66a42b78a23f21169a11e9a21f054a6b761e551a6ba3d941162b0c9031957b75", - "sig": "6112693f17f2bc0e990b53a681278c334ac57d85ebb1bb5b27939154b0f3c9490890bffac843fa6c584cfb1d1830e77c86fc2a7dfcaf4ba26aab8b965ea2a0f1" + "sig": "af8ce33d4b3707bd6e5a884d2cb58b40e692ebd1c2523961371ccc15fabedd39f9aafcfd89718dbf00d0ea6f41538c14cf69b331c26ffbf5b101a4a8f5d0b81b" }, { "kind": 30023, @@ -1438,7 +1438,7 @@ "content": "Flood body 46.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "41fbc8d8876f82e73421aaedd7ce5493bd3bde0a5e4f115e82ce584f447da8db", - "sig": "4dd61689ae35fb2fe0586d46b3b369c55746b5fc1d3a3e5e246aa406679947776a0e2e79832b121f8233891ba0f17f5d5a7c670c876c8d352dde1a0cf56a5517" + "sig": "cbd77b64d49a53b9265a9278899d1e80db9683635182136b7270b576461c44a90c3c960aefb04b6a3984b4aa5ae10333cac78566584a5115b6059678cf270ce2" }, { "kind": 30023, @@ -1456,7 +1456,7 @@ "content": "Flood body 47.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "971010572866054b8e0d914dfc191fb7da48b391b228f9e2880b201ff88909db", - "sig": "3c4b35c01152435ea72e50fd54594087a3ceb8a13e0b61a3e6f993931c87aba268672f417f8cd0b6b9b145b20f1b69a1d83d8b31efd030e710e7b83fc4752160" + "sig": "6465a4c1fab710d3be523fab6856f98e0693da5bfec677325306afa95167825ffbb6c4fa4e0d401f8ff01da392b8eaaf11c10b6bc4f689cc20af32514d741b44" }, { "kind": 30023, @@ -1474,7 +1474,7 @@ "content": "Flood body 48.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d113ee6a51de6ba2e352640e6a6267cce7b4c9467123f1a70b7bb698bdaa6dd6", - "sig": "085dd5a04768aec1e68475d1e3ec2ac110b09ef08877e62912e81e2aa9cc19b26658246a7ba235dabba0ec9237d3b52589b0d1903bb32735a30bf9ae7d723874" + "sig": "b65119943ee5e926b06653335a70055eac3967f9a625356e7346f2debbbd3df6441fdee8ec892901bedeb43fd2f8e95880e6f4e7eb4981d0e5564bc556ed8db1" }, { "kind": 30023, @@ -1492,7 +1492,7 @@ "content": "Flood body 49.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "1e9d9ff7a10dd180b4ec9172b7470c9084b80a199f031c9f4f88053081998414", - "sig": "ed303111689067b93acf39c30517f7d7737708ac0a937d95fa09f22ba25c56fe0e1e96a9311b2ba970abc3e107fc43f9a2cdc6d6208559c86520de5c5461daf3" + "sig": "f9183df9c3656d49fe76a2851c9ec9afc008063c3d99cc556aff1974b13c7a2350c2220872dee20b1687d1229520a70abbc1271a2ee86fcc042b1aa0b4bd4df1" }, { "kind": 30023, @@ -1510,7 +1510,7 @@ "content": "Flood body 50.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "813e50747e2e8af3cd1937b70ce0b95a1717ad143d8ada7f3ae0452ae1dd067d", - "sig": "1d89d43a904be445622fba4966efcd69aea51dd62702375b5e23b256b009f084ae4ebf36110d7c965e821ec57aad12142d258bc036083d7f063475686caea961" + "sig": "c99467c016d6214a281e560b8a06e22d2fe35b76f973677988f2dc9fc75277952c2ccfbad78650b13ed956e35696899de77d63ab1c5caa617a04ecab9dc6849f" }, { "kind": 30023, @@ -1528,7 +1528,7 @@ "content": "Flood body 51.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d3ccdaa50ba1347393221e3acbcd912a4efd81d33b5988471ae98b7a2c4f5986", - "sig": "3c30b64baea31223c74ef8fa02a331d2a6cdd8a05e81e2a1c2006adb9f09f40cbd5fa70726860f086f663c1c9ffbe7352acff72c2630b82fb298e9d4197692cb" + "sig": "50157b4904fbd4cc564eff367904cc4fa6ec4193a7df7343b58ded99bd92aeaa7acd62e7855aa287f5517046f8deb38ec04cea69881c218095ac2dbc2007c975" }, { "kind": 30023, @@ -1546,7 +1546,7 @@ "content": "Flood body 52.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "2daf4ea0e3a19112859fa9ab7fa032d99728123ae7a61d5f71dce2ae44fb5ff6", - "sig": "23097fc8c80602643c1aceaed891254a49562813520920f4375b47d9536a44e96b6b30c1e600651faf3afa122fa3db2866242c66eedf42304882e2f75e1c3aa4" + "sig": "6d039d271b7f6e43fdd7f6d26d1821205c78fc8da77bcb7fe9971c2d0da7b1b3f521a4949e66743b763e18351a1030ad198cde2ac0cb6b8ae80df65b080874be" }, { "kind": 30023, @@ -1564,7 +1564,7 @@ "content": "Flood body 53.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "06d9d9e326ca5fe1ce2ec408158cca2a8ffb8b3d0a3f520285cd4c1d49bf0105", - "sig": "1a7bb0b95db8768550cdf2efcc81b24a5d360debeaaaa8f7810f67d5dfd4c303f6ab740baa77dc0a1559c4b7d4bc1f1b250a4700602397f6ed1e68e6a63fadea" + "sig": "8afc9b6c81cc7de20c9d779a90db50a4ea1857b8d07ed32acc51e1bcf8ed8eb57e5fbb05013fff5ecec13acf702686dc29c6d31125f2f4238413677ea5c75ca9" }, { "kind": 30023, @@ -1582,7 +1582,7 @@ "content": "Flood body 54.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "7e60271536d203c459c40b72a8f58de4675f1c17721540a3c9deecf715a1524b", - "sig": "5152bd31bdb208cbce85dadca644c2386a9709ef5bd18a0328a3351bb74a681e9775753bc3c5645d133d74eb6965f309e758cb7bbe48990f9835646bba42cdb2" + "sig": "94ec81eecd94b73ee398e09d7a766e9e95cd374b4aaa42c9531422742c938beea7535ce56fcef4bb9331ef4fa85c22e69064e0cabd470c1c18f0b11c4c342b71" }, { "kind": 30023, @@ -1600,7 +1600,7 @@ "content": "Flood body 55.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "f1b2fc6905a2ba18bfe39dcc8734e61df52d7f2186c0cf5bf8bfa1656ef5a60b", - "sig": "35faabb4db01ba04b01b3534e6084dc3926faf3a5e8fd53a4b1d7b4757d8abb61acc3972971e0b3e45bc810786f7b64b50679378ef1dc6986aa7873d269c649d" + "sig": "cd8e7d2c863a008462e0d4a23d8060dabb2b2eee3d230e9badf4293b81e68fd0b8f2ecd18a1e3731d0df4b0388472aa998ddcac62d948457b4aab110e22e28b3" }, { "kind": 30023, @@ -1618,7 +1618,7 @@ "content": "Flood body 56.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "a944f2245c3b9c337df0cb4ac7840f73174360581e944eb46e26565093b6a5a3", - "sig": "adaa06e30a966a3729c13a29b54dc4b9f60772a7672389ecd5f391c67325ee5f0bfe4c9f1b06857afe89639c7f22f1888cb4dede52e3ef5ffd41e45ca054e772" + "sig": "aecf735bc54a8c946024650145cf61aa402e7d12cc76c47753adcdf1f402b05542409ca7e5f29f41f22eeb376c22e18375610115ae2da1355325a170e2fe3792" }, { "kind": 30023, @@ -1636,7 +1636,7 @@ "content": "Flood body 57.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "9a259980049308755793f8be4a04ce8473f2f413a7b4390be7e27bef5178b537", - "sig": "b257fd7af3ae875bc23d3a37c72eb5bc96c47191c5a89a57ca7b557aef44c9021cb7c4fea00855e97aba1cd793dc7d38b2fa1600f1861f6bae9a639692d20b7c" + "sig": "e274bad094bfdf7a20f93150bfc41c76cace72f56c264e35e0f9faec563aefd31077fb3f06924fc9365b998ce75c6a7eaeb45535af022ce9e60d0c22da39ddb3" }, { "kind": 30023, @@ -1654,7 +1654,7 @@ "content": "Flood body 58.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "e3191e3783d0de772d8ed653002fe8aa7e692a208b5181594cab4610620d8307", - "sig": "29e32d44f61b6b39f2dc1ef6d4d4925f496031e970303d62728470e0487526589864851bb26b1ef7a37b1182552e891f435ca5018707083b01a749b21c0bc4ce" + "sig": "bb65d283c43f84d5a406c384bbbf45b0bc5d0d504bdf54f6deaee877f90d4513a50fbf2b719e00396e9b9dc7be0bd74d43a23f1f71b5f3217f50a276529078ef" }, { "kind": 30023, @@ -1672,7 +1672,7 @@ "content": "Flood body 59.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "d063e1f2f695d2e2e445124d1d9aaef8cbef83739c34970994ee36249a158424", - "sig": "236f3c432b1093217fa562c397318a815889e9ddf143183135b01553992494f7282e9e4579eb9e3640b717ae3bc3cb32e0d537497366be325a527e70754dba1f" + "sig": "22458876ae7f5b0e16f777807dec3f6d7055fb87bf57c1d727ab5af74d12178ee6856dcbccc3e331189af9899b9c7bda0623782676cecad63e87e8558bd740da" }, { "kind": 30023, @@ -1690,7 +1690,7 @@ "content": "Flood body 60.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "1ca11c97f1af3df8e561beddef29cfae62e8a83eea7bcdd77762f2b771bed3a6", - "sig": "d3d47a25be1d3f0bb1aaf7aed25feea65b297d4e7c80f596ab4db46cfd810252837cc0bf44f845bfc2bef68b7d071b677d037455ed22322f8675c57f936a0507" + "sig": "a9909193a5840922b8a400fadf7d4b851464fc883ac6f5b9dfe19f4c3b08678ce6891c7d0de2c17498fa4e973d88f7ade152702100c28bd9e3cfcb8df4014f2b" }, { "kind": 30023, @@ -1708,7 +1708,7 @@ "content": "Flood body 61.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "24783e4bf579fafef3c7cc8841d596328eec40fe806ff3197d27f061fce759e2", - "sig": "275fc2993f798ebb0151fcd28e891bcb00b69bb1084fb6c6e38276a71e923d1bf46821a282d6afc4d42edf9c24498659d7ecb63168ce6828e279372529cd2dfd" + "sig": "70acd739607e51a30f83ccec582051b75562ad1c43aacd94431837671cdab1bc7dccb515dbc082a1281140250f585d1536fa919141ed622d05fae52221bcde92" }, { "kind": 30023, @@ -1726,7 +1726,7 @@ "content": "Flood body 62.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "8812909cbd074fd25bb74880c4fb60a68d0211e6092e684183ecf520ba0289dd", - "sig": "9215223eeeaddc27f3548109763c6cb25814ed2c04e37cc5c8d03eecd02809a5b1e564394644757ce84228faba97e9512a0e1b0c898b346e31ddbcaa1539c5d1" + "sig": "78e42d599a2056f18b816677da4a2d7a3c57e238c6b76c262350a8d7ca32d39f259389a3dae3be936dddfd9ef9adf10da69ad569c5f909604b3e85f6abe74de9" }, { "kind": 30023, @@ -1744,7 +1744,7 @@ "content": "Flood body 63.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "c17adf372a9a21f91c07defb9d7bc5edb7b7c7429e2f65f448bf5af2a2f54687", - "sig": "c395812ae0c773896ce7d3fe2d3e26e573fe1d3bc0e0f5bd60c1611eb0706d74e50f599834a312880625d25579a9e8e938166f7b7c1638c4d221aa77c22da4c1" + "sig": "c9943ab49413596ba917fbe82d62be1835d1ad344c2aeeb41f8a0328b5723d1f7fd173d2fabc8549ec903e5268311bda1ba158e81a8e6e2dbdfd104eefdf465d" }, { "kind": 30023, @@ -1762,7 +1762,7 @@ "content": "Flood body 64.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5428cd73b5c818990dbf01b19a7508dbbda4582e0ccec6e1f68f8484619f0a86", - "sig": "61b89fbd19f270d9387a8c1ca39a0005791c3a14a9e1a83333bda9541a7d95f1494ac012f8daed29f3d8c6b15ffa7058555cfd89a466b97d367b12429ace30bc" + "sig": "ecb9e06cb03c2838a81bf89eb1c5800b06ec6b2fb5a8f0f407b1338465dea83f00138ead3d09987b34cffcde4b3e2b3290bd4c8a32d0aad9b40d136a366e6cdb" }, { "kind": 30023, @@ -1780,7 +1780,7 @@ "content": "Flood body 65.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "id": "5bdcb1394bdf9320465bfe4d819d5b8cf4c826a95bca1a9b8c9405ffa5a00ea1", - "sig": "e27855aef566455d6373a9aa98ef6f19966500d91969cbb9ee8237b2d6b8c9a6ac291b8981a6eb69bfeebea62d6a8afbf96c54a9f7c1e0f7bf9e2c73a9c0c889" + "sig": "3b25661b5e0443ed0ad5768e98a52bf6dc725cac3b49f23e81b1a59a9dfe4391f1a4bc9e45f3a918d2f38dafdc079f32513a22645039f1df23be399e32079c3f" } ] }, @@ -1801,7 +1801,7 @@ ], [ "summary", - "Alice's first Nostrbook test post" + "Alice's first nbread.lol test post" ], [ "published_at", @@ -1810,8 +1810,8 @@ ], "content": "# Hello world\n\nThis is **alice**'s first post.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "id": "1a9ef505e58fd74986ee324ffd74bab15fc9d37244613b42b7f00fc49d6c4330", - "sig": "6ee053b9132287b978a6ca833a9e4cbff1e896e123cd452cead0f5609a84c7e336d5575eb599cf3714de1680d75eb82021216381774d31c6b3d523df696f79f0" + "id": "6d915700436c2f0203f9c5cc93ae8669a344bd01cb5505b0ac41e15370b31e69", + "sig": "0ea768b79b261d208a39ab674e0e34fe05864dcab184d699361625ffe702a8f3dcb3d5010adc0e1f2a4080af7811ea99a276ac7ef65ac1ee77cd53e2a9d028e0" } }, { @@ -1830,7 +1830,7 @@ ], [ "summary", - "Alice's first Nostrbook test post" + "Alice's first nbread.lol test post" ], [ "published_at", @@ -1839,8 +1839,8 @@ ], "content": "# Hello world\n\nThis is **alice**'s first post.\n", "pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "id": "1a9ef505e58fd74986ee324ffd74bab15fc9d37244613b42b7f00fc49d6c4331", - "sig": "6ee053b9132287b978a6ca833a9e4cbff1e896e123cd452cead0f5609a84c7e336d5575eb599cf3714de1680d75eb82021216381774d31c6b3d523df696f79f6" + "id": "6d915700436c2f0203f9c5cc93ae8669a344bd01cb5505b0ac41e15370b31e60", + "sig": "0ea768b79b261d208a39ab674e0e34fe05864dcab184d699361625ffe702a8f3dcb3d5010adc0e1f2a4080af7811ea99a276ac7ef65ac1ee77cd53e2a9d028e4" } }, { @@ -1859,7 +1859,7 @@ ], [ "summary", - "Alice's first Nostrbook test post" + "Alice's first nbread.lol test post" ], [ "published_at", @@ -1868,8 +1868,8 @@ ], "content": "# Hello world\n\nThis is **alice**'s first post.\n", "pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "id": "7007cfb43ed114869989f41c68b9e35fcf02cfee2b7dba88dd06668ff8aa4269", - "sig": "6ee053b9132287b978a6ca833a9e4cbff1e896e123cd452cead0f5609a84c7e336d5575eb599cf3714de1680d75eb82021216381774d31c6b3d523df696f79f6" + "id": "62119b641f2bd8467704dd18462f41433cc5fecdc5b8fb0737161e1fcc021180", + "sig": "0ea768b79b261d208a39ab674e0e34fe05864dcab184d699361625ffe702a8f3dcb3d5010adc0e1f2a4080af7811ea99a276ac7ef65ac1ee77cd53e2a9d028e4" } } ] diff --git a/test/fixtures/keys.json b/test/fixtures/keys.json index c1fccbe..f141dcf 100644 --- a/test/fixtures/keys.json +++ b/test/fixtures/keys.json @@ -1,5 +1,5 @@ { - "WARNING": "THROWAWAY TEST KEYS for Nostrbook fixtures only. Trivial patterns, publicly committed — NEVER use for anything real.", + "WARNING": "THROWAWAY TEST KEYS for nbread.lol fixtures only. Trivial patterns, publicly committed — NEVER use for anything real.", "alice": { "sk": "0101010101010101010101010101010101010101010101010101010101010101", "pk": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f" diff --git a/test/helpers.ts b/test/helpers.ts index d95a565..11015de 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -105,7 +105,7 @@ export function signLoginEvent( ): NostrEvent { const tags: string[][] = []; if (opts.relay !== null) { - tags.push(["relay", opts.relay ?? "wss://nostrbook.net"]); + tags.push(["relay", opts.relay ?? "wss://nbread.lol"]); } tags.push(["challenge", challenge]); return finalizeEvent( @@ -121,7 +121,7 @@ export function signLoginEvent( /** Fetch a login challenge nonce from the worker. */ export async function getChallenge(ip = "10.0.0.1"): Promise { - const res = await SELF.fetch("https://nostrbook.net/login/challenge", { + const res = await SELF.fetch("https://nbread.lol/login/challenge", { headers: { "CF-Connecting-IP": ip }, }); if (res.status !== 200) { @@ -141,7 +141,7 @@ export function postLogin( "CF-Connecting-IP": opts.ip ?? "10.0.0.1", }; if (opts.cookie) headers.Cookie = opts.cookie; - return SELF.fetch("https://nostrbook.net/login", { + return SELF.fetch("https://nbread.lol/login", { method: "POST", headers, body: typeof event === "string" ? event : JSON.stringify(event), @@ -206,7 +206,7 @@ export function signDeleteEvent(opts: { kind: 5, created_at: opts.created_at, tags, - content: "Deleted via Nostrbook", + content: "Deleted via nbread.lol", }, hexToBytes(opts.sk ?? ALICE_SK), ) as NostrEvent; diff --git a/test/integration/__snapshots__/discover.spec.ts.snap b/test/integration/__snapshots__/discover.spec.ts.snap index b76d12e..1d36ba3 100644 --- a/test/integration/__snapshots__/discover.spec.ts.snap +++ b/test/integration/__snapshots__/discover.spec.ts.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`discover feed (P6) > matches the snapshot 1`] = `"Discover — Nostrbook

    Discover

    Recent posts from blogs on nostrbook.net.

    "`; +exports[`discover feed (P6) > matches the snapshot 1`] = `"Discover — nbread.lol

    Discover

    Recent posts from blogs on nbread.lol.

    "`; -exports[`landing page (P6 polish) > pitches the product and carries the login CTA 1`] = `"Nostrbook — Nostr-native blogging

    Nostrbook

    Nostr-native, no-nonsense, super-fast blogging.

    Your posts are NIP-23 events signed by your key — we just render them beautifully at you.nostrbook.net.

    Sign in with NostrDiscover blogs →

    • Your keys, your words — every post is a signed event on public relays; leave any time and take everything with you.
    • A real blog, not a feed — a clean site at handle.nostrbook.net with RSS, Atom, and a sitemap.
    • Free NIP-05 — be handle@nostrbook.net everywhere on Nostr.
    • Write anywhere — the built-in markdown editor with NIP-07 signing, or any Nostr client; your blog stays in sync with the relays.
    • Make it yours — custom theme CSS, no build step required.
    • No ads, no trackers, no JavaScript on blogs — tiny pages, fast everywhere.
    "`; +exports[`landing page (P6 polish) > pitches the product and carries the login CTA 1`] = `"nbread.lol — Nostr-native blogging

    nbread.lol

    Nostr-native, no-nonsense, super-fast blogging.

    Your posts are NIP-23 events signed by your key — we just render them beautifully at you.nbread.lol.

    Sign in with NostrDiscover blogs →

    • Your keys, your words — every post is a signed event on public relays; leave any time and take everything with you.
    • A real blog, not a feed — a clean site at handle.nbread.lol with RSS, Atom, and a sitemap.
    • Free NIP-05 — be handle@nbread.lol everywhere on Nostr.
    • Write anywhere — the built-in markdown editor with NIP-07 signing, or any Nostr client; your blog stays in sync with the relays.
    • Make it yours — custom theme CSS, no build step required.
    • No ads, no trackers, no JavaScript on blogs — tiny pages, fast everywhere.
    "`; diff --git a/test/integration/__snapshots__/search.spec.ts.snap b/test/integration/__snapshots__/search.spec.ts.snap index 0ebcae1..b278f72 100644 --- a/test/integration/__snapshots__/search.spec.ts.snap +++ b/test/integration/__snapshots__/search.spec.ts.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`search (P6) > search page > renders results with links to the posts' blog URLs 1`] = `"Search — Nostrbook

    Search

    1 result for “hello”

    "`; +exports[`search (P6) > search page > renders results with links to the posts' blog URLs 1`] = `"Search — nbread.lol

    Search

    1 result for “hello”

    "`; -exports[`search (P6) > search page > serves the bare form without a query (no results section) 1`] = `"Search — Nostrbook

    Search

    Search every post published on nostrbook.net.

    "`; +exports[`search (P6) > search page > serves the bare form without a query (no results section) 1`] = `"Search — nbread.lol

    Search

    Search every post published on nbread.lol.

    "`; diff --git a/test/integration/__snapshots__/tenant.spec.ts.snap b/test/integration/__snapshots__/tenant.spec.ts.snap index b43e8cd..735ac95 100644 --- a/test/integration/__snapshots__/tenant.spec.ts.snap +++ b/test/integration/__snapshots__/tenant.spec.ts.snap @@ -1,15 +1,15 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`blog subdomain rendering (SELF.fetch) > blog home > lists posts with title, summary and date 1`] = `"alice-test — Nostrbook

    alice-test

    @alice

    Nostrbook throwaway test profile (alice)

    "`; +exports[`blog subdomain rendering (SELF.fetch) > blog home > lists posts with title, summary and date 1`] = `"alice-test — nbread.lol

    alice-test

    @alice

    nbread.lol throwaway test profile (alice)

    "`; exports[`blog subdomain rendering (SELF.fetch) > feeds and crawler files > serves a well-formed sitemap.xml 1`] = ` " -https://alice.nostrbook.net/ -https://alice.nostrbook.net/escaping-torture2023-11-14T22:20:50.000Z -https://alice.nostrbook.net/xss-test2023-11-14T22:18:20.000Z -https://alice.nostrbook.net/markdown-torture2023-11-14T22:16:40.000Z -https://alice.nostrbook.net/hello-world2023-11-14T22:15:00.000Z +https://alice.nbread.lol/ +https://alice.nbread.lol/escaping-torture2023-11-14T22:20:50.000Z +https://alice.nbread.lol/xss-test2023-11-14T22:18:20.000Z +https://alice.nbread.lol/markdown-torture2023-11-14T22:16:40.000Z +https://alice.nbread.lol/hello-world2023-11-14T22:15:00.000Z " `; @@ -19,21 +19,21 @@ exports[`blog subdomain rendering (SELF.fetch) > feeds and crawler files > serve alice-test -https://alice.nostrbook.net/ -Nostrbook throwaway test profile (alice) - +https://alice.nbread.lol/ +nbread.lol throwaway test profile (alice) + Tue, 14 Nov 2023 22:20:50 GMT -Escaping "torture" 
 titlehttps://alice.nostrbook.net/escaping-torturehttps://alice.nostrbook.net/escaping-tortureTue, 14 Nov 2023 22:20:50 GMTControl chars, line separators, and non-BMP in content -XSS <script>alert('title')</script> torturehttps://alice.nostrbook.net/xss-testhttps://alice.nostrbook.net/xss-testTue, 14 Nov 2023 22:18:20 GMT<img src=x onerror=alert('summary')> -Markdown torture testhttps://alice.nostrbook.net/markdown-torturehttps://alice.nostrbook.net/markdown-tortureTue, 14 Nov 2023 22:16:40 GMTEvery markdown feature in one post -Hello worldhttps://alice.nostrbook.net/hello-worldhttps://alice.nostrbook.net/hello-worldTue, 14 Nov 2023 22:15:00 GMTAlice's first Nostrbook test post +Escaping "torture" 
 titlehttps://alice.nbread.lol/escaping-torturehttps://alice.nbread.lol/escaping-tortureTue, 14 Nov 2023 22:20:50 GMTControl chars, line separators, and non-BMP in content +XSS <script>alert('title')</script> torturehttps://alice.nbread.lol/xss-testhttps://alice.nbread.lol/xss-testTue, 14 Nov 2023 22:18:20 GMT<img src=x onerror=alert('summary')> +Markdown torture testhttps://alice.nbread.lol/markdown-torturehttps://alice.nbread.lol/markdown-tortureTue, 14 Nov 2023 22:16:40 GMTEvery markdown feature in one post +Hello worldhttps://alice.nbread.lol/hello-worldhttps://alice.nbread.lol/hello-worldTue, 14 Nov 2023 22:15:00 GMTAlice's first nbread.lol test post " `; exports[`blog subdomain rendering (SELF.fetch) > post page > renders title, date and content 1`] = ` -"Hello world — @alice

    alice-test

    @alice

    Nostrbook throwaway test profile (alice)

    Hello world

    Hello world

    +"Hello world — @alice

    alice-test

    @alice

    nbread.lol throwaway test profile (alice)

    Hello world

    Hello world

    This is alice's first post.

    -
    " +
    " `; diff --git a/test/integration/admin.spec.ts b/test/integration/admin.spec.ts index ee101e2..4b725af 100644 --- a/test/integration/admin.spec.ts +++ b/test/integration/admin.spec.ts @@ -44,7 +44,7 @@ function postAdmin( const headers: Record = {}; if (opts.cookie) headers.Cookie = opts.cookie; if (opts.origin) headers.Origin = opts.origin; - return SELF.fetch(`https://nostrbook.net${path}`, { + return SELF.fetch(`https://nbread.lol${path}`, { method: "POST", headers, body: new URLSearchParams({ target }), @@ -97,12 +97,12 @@ describe("admin gate", () => { }); it("anonymous callers get 404", async () => { - const res = await SELF.fetch("https://nostrbook.net/admin"); + const res = await SELF.fetch("https://nbread.lol/admin"); expect(res.status).toBe(404); }); it("non-admin sessions (mallory) get 404 on GET and POST", async () => { - const page = await SELF.fetch("https://nostrbook.net/admin", { + const page = await SELF.fetch("https://nbread.lol/admin", { headers: { Cookie: malloryCookie }, }); expect(page.status).toBe(404); @@ -113,7 +113,7 @@ describe("admin gate", () => { }); it("the admin session gets the page", async () => { - const res = await SELF.fetch("https://nostrbook.net/admin", { + const res = await SELF.fetch("https://nbread.lol/admin", { headers: { Cookie: adminCookie }, }); expect(res.status).toBe(200); @@ -123,7 +123,7 @@ describe("admin gate", () => { it("unset ADMIN_PUBKEY disables the surface entirely (even for the admin)", async () => { for (const value of ["", "not-a-key"]) { const res = await app.fetch( - new Request("https://nostrbook.net/admin", { + new Request("https://nbread.lol/admin", { headers: { Cookie: adminCookie }, }), { ...env, ADMIN_PUBKEY: value }, @@ -189,23 +189,23 @@ describe("admin gate", () => { describe("block → everywhere → unblock (alice, claimed)", () => { it("runs the full lifecycle", async () => { // --- Pre-block state: everything visible ----------------------------- - const warm1 = await SELF.fetch("https://alice.nostrbook.net/"); + const warm1 = await SELF.fetch("https://alice.nbread.lol/"); expect(warm1.status).toBe(200); - const warm2 = await SELF.fetch("https://alice.nostrbook.net/"); - expect(warm2.headers.get("X-Nostrbook-Cache")).toBe("hit"); // page is CACHED + const warm2 = await SELF.fetch("https://alice.nbread.lol/"); + expect(warm2.headers.get("X-Nbread-Cache")).toBe("hit"); // page is CACHED await resetDiscoverCache(); - const discoverBefore = await SELF.fetch("https://nostrbook.net/discover"); + const discoverBefore = await SELF.fetch("https://nbread.lol/discover"); expect(await discoverBefore.text()).toContain("Hello world"); const searchBefore = await SELF.fetch( - "https://nostrbook.net/search?q=hello", + "https://nbread.lol/search?q=hello", { headers: { "CF-Connecting-IP": "10.1.1.1" } }, ); expect(await searchBefore.text()).toContain("Hello world"); const nip05Before = await SELF.fetch( - "https://nostrbook.net/.well-known/nostr.json?name=alice", + "https://nbread.lol/.well-known/nostr.json?name=alice", ); expect(((await nip05Before.json()) as { names: Record }).names.alice).toBe( ALICE_PK, @@ -224,31 +224,31 @@ describe("block → everywhere → unblock (alice, claimed)", () => { expect(await env.KV.get(`gen:${ALICE_PK}`)).not.toBe(genBefore); // Blog host 404s — INCLUDING the page that was just served from cache. - const blog = await SELF.fetch("https://alice.nostrbook.net/"); + const blog = await SELF.fetch("https://alice.nbread.lol/"); expect(blog.status).toBe(404); - const post = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const post = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(post.status).toBe(404); // npub view 404s (claimed handles normally redirect; blocked → 404). const npubView = await SELF.fetch( - `https://nostrbook.net/${npubEncode(ALICE_PK)}`, + `https://nbread.lol/${npubEncode(ALICE_PK)}`, { headers: { "CF-Connecting-IP": "10.1.1.2" } }, ); expect(npubView.status).toBe(404); // Dropped from discover (page cache purged → fresh query) and search. await resetDiscoverCache(); - const discoverAfter = await SELF.fetch("https://nostrbook.net/discover"); + const discoverAfter = await SELF.fetch("https://nbread.lol/discover"); expect(await discoverAfter.text()).not.toContain("Hello world"); const searchAfter = await SELF.fetch( - "https://nostrbook.net/search?q=hello", + "https://nbread.lol/search?q=hello", { headers: { "CF-Connecting-IP": "10.1.1.1" } }, ); expect(await searchAfter.text()).not.toContain("Hello world"); // NIP-05: blocked users are DROPPED from nostr.json (documented policy). const nip05After = await SELF.fetch( - "https://nostrbook.net/.well-known/nostr.json?name=alice", + "https://nbread.lol/.well-known/nostr.json?name=alice", ); expect((await nip05After.json()) as object).toEqual({ names: {} }); @@ -260,7 +260,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { content: "should not land", created_at: Math.floor(Date.now() / 1000), }); - const mirror = await SELF.fetch("https://nostrbook.net/api/mirror", { + const mirror = await SELF.fetch("https://nbread.lol/api/mirror", { method: "POST", headers: { "Content-Type": "application/json", Cookie: aliceCookie }, body: JSON.stringify(signed), @@ -269,7 +269,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { // …settings (P5 gate)… const settings = await SELF.fetch( - "https://nostrbook.net/dashboard/settings", + "https://nbread.lol/dashboard/settings", { method: "POST", headers: { Cookie: aliceCookie }, @@ -282,7 +282,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { // to run renderPost on the request path refuses blocked keys — blocked // identities must not spend request-path CPU)… const preview = await SELF.fetch( - "https://nostrbook.net/dashboard/preview", + "https://nbread.lol/dashboard/preview", { method: "POST", headers: { "Content-Type": "application/json", Cookie: aliceCookie }, @@ -293,7 +293,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { // …and claim (P4 gate — alice already holds a handle, but the blocked // check fires first and hides even the "already claimed" signal). - const claim = await SELF.fetch("https://nostrbook.net/dashboard/claim", { + const claim = await SELF.fetch("https://nbread.lol/dashboard/claim", { method: "POST", headers: { Cookie: aliceCookie, "CF-Connecting-IP": "10.1.1.3" }, body: new URLSearchParams({ handle: "alice2" }), @@ -309,13 +309,13 @@ describe("block → everywhere → unblock (alice, claimed)", () => { ); expect(unblock.status).toBe(303); - const restored = await SELF.fetch("https://alice.nostrbook.net/"); + const restored = await SELF.fetch("https://alice.nbread.lol/"); expect(restored.status).toBe(200); expect(await restored.text()).toContain("Hello world"); // Preview works again post-unblock (blocked gate, not a lingering state). const previewRestored = await SELF.fetch( - "https://nostrbook.net/dashboard/preview", + "https://nbread.lol/dashboard/preview", { method: "POST", headers: { "Content-Type": "application/json", Cookie: aliceCookie }, @@ -325,7 +325,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { expect(previewRestored.status).toBe(200); const nip05Restored = await SELF.fetch( - "https://nostrbook.net/.well-known/nostr.json?name=alice", + "https://nbread.lol/.well-known/nostr.json?name=alice", ); expect( ((await nip05Restored.json()) as { names: Record }) @@ -334,7 +334,7 @@ describe("block → everywhere → unblock (alice, claimed)", () => { await resetDiscoverCache(); const discoverRestored = await SELF.fetch( - "https://nostrbook.net/discover", + "https://nbread.lol/discover", ); expect(await discoverRestored.text()).toContain("Hello world"); }); @@ -352,13 +352,13 @@ describe("block by npub (mallory, never claimed)", () => { // The npub view 404s WITHOUT any relay contact (resolveNpub checks the // users row first). const view = await SELF.fetch( - `https://nostrbook.net/${npubEncode(MALLORY_PK)}`, + `https://nbread.lol/${npubEncode(MALLORY_PK)}`, { headers: { "CF-Connecting-IP": "10.1.2.1" } }, ); expect(view.status).toBe(404); // The blocked key cannot claim a handle. - const claim = await SELF.fetch("https://nostrbook.net/dashboard/claim", { + const claim = await SELF.fetch("https://nbread.lol/dashboard/claim", { method: "POST", headers: { Cookie: malloryCookie, "CF-Connecting-IP": "10.1.2.2" }, body: new URLSearchParams({ handle: "mallory" }), @@ -367,7 +367,7 @@ describe("block by npub (mallory, never claimed)", () => { expect(await claim.text()).toContain("blocked"); // The admin page lists the blocked key for later unblocking. - const page = await SELF.fetch("https://nostrbook.net/admin", { + const page = await SELF.fetch("https://nbread.lol/admin", { headers: { Cookie: adminCookie }, }); expect(await page.text()).toContain(npubEncode(MALLORY_PK)); @@ -378,7 +378,7 @@ describe("block by npub (mallory, never claimed)", () => { { cookie: adminCookie }, ); expect(unblock.status).toBe(303); - const claim2 = await SELF.fetch("https://nostrbook.net/dashboard/claim", { + const claim2 = await SELF.fetch("https://nbread.lol/dashboard/claim", { method: "POST", headers: { Cookie: malloryCookie, "CF-Connecting-IP": "10.1.2.3" }, body: new URLSearchParams({ handle: "mallory" }), diff --git a/test/integration/app.spec.ts b/test/integration/app.spec.ts index d421ce1..cf5dc61 100644 --- a/test/integration/app.spec.ts +++ b/test/integration/app.spec.ts @@ -10,24 +10,24 @@ describe("worker end-to-end (SELF.fetch)", () => { await seedAlice(); }); - it("serves the main site on https://nostrbook.net/", async () => { - const res = await SELF.fetch("https://nostrbook.net/"); + it("serves the main site on https://nbread.lol/", async () => { + const res = await SELF.fetch("https://nbread.lol/"); expect(res.status).toBe(200); const html = await res.text(); - expect(html).toContain("Nostrbook"); + expect(html).toContain("nbread.lol"); expect(html).toContain(" { - const res = await SELF.fetch("https://nostrbook.net/healthz"); + const res = await SELF.fetch("https://nbread.lol/healthz"); expect(res.status).toBe(200); const body = (await res.json()) as { ok: boolean; service: string }; expect(body.ok).toBe(true); - expect(body.service).toBe("nostrbook"); + expect(body.service).toBe("nbread"); }); - it("serves a distinct blog page on https://alice.nostrbook.net/", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/"); + it("serves a distinct blog page on https://alice.nbread.lol/", async () => { + const res = await SELF.fetch("https://alice.nbread.lol/"); expect(res.status).toBe(200); const html = await res.text(); expect(html).toContain("@alice"); @@ -35,18 +35,18 @@ describe("worker end-to-end (SELF.fetch)", () => { expect(html).not.toContain("Nostr-native blogging"); }); - it("404s an unclaimed subdomain https://unknown.nostrbook.net/", async () => { - const res = await SELF.fetch("https://unknown.nostrbook.net/"); + it("404s an unclaimed subdomain https://unknown.nbread.lol/", async () => { + const res = await SELF.fetch("https://unknown.nbread.lol/"); expect(res.status).toBe(404); }); - it("404s a spoofed host https://nostrbook.net.evil.com/", async () => { - const res = await SELF.fetch("https://nostrbook.net.evil.com/"); + it("404s a spoofed host https://nbread.lol.evil.com/", async () => { + const res = await SELF.fetch("https://nbread.lol.evil.com/"); expect(res.status).toBe(404); }); it("the mirror API exists on the apex and gates on the session (401 anon)", async () => { - const mirror = await SELF.fetch("https://nostrbook.net/api/mirror", { + const mirror = await SELF.fetch("https://nbread.lol/api/mirror", { method: "POST", }); expect(mirror.status).toBe(401); // P5: session required, not a 404/501 stub @@ -55,9 +55,9 @@ describe("worker end-to-end (SELF.fetch)", () => { it("apex auth/dashboard routes are not exposed on blog subdomains", async () => { // On a blog subdomain these fall through to the tenant slug route and // 404 (no such post) — the apex login/dashboard pages must not render. - const login = await SELF.fetch("https://alice.nostrbook.net/login"); + const login = await SELF.fetch("https://alice.nbread.lol/login"); expect(login.status).toBe(404); - const dashboard = await SELF.fetch("https://alice.nostrbook.net/dashboard"); + const dashboard = await SELF.fetch("https://alice.nbread.lol/dashboard"); expect(dashboard.status).toBe(404); }); }); diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 9f35014..73ee1d8 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -76,7 +76,7 @@ beforeEach(async () => { describe("GET /login", () => { it("serves the login page with the NIP-07 glue", async () => { - const res = await SELF.fetch("https://nostrbook.net/login"); + const res = await SELF.fetch("https://nbread.lol/login"); expect(res.status).toBe(200); const html = await res.text(); expect(html).toContain("login-button"); @@ -87,7 +87,7 @@ describe("GET /login", () => { const challenge = await getChallenge(); const login = await postLogin(signLoginEvent(challenge)); expect(login.status).toBe(200); - const res = await SELF.fetch("https://nostrbook.net/login", { + const res = await SELF.fetch("https://nbread.lol/login", { headers: { Cookie: `sid=${sidFrom(login)}` }, redirect: "manual", }); @@ -99,7 +99,7 @@ describe("GET /login", () => { describe("GET /login/challenge", () => { it("issues a 64-hex nonce stored in D1 with a 5min expiry", async () => { const before = now(); - const res = await SELF.fetch("https://nostrbook.net/login/challenge"); + const res = await SELF.fetch("https://nbread.lol/login/challenge"); expect(res.status).toBe(200); const body = (await res.json()) as { challenge: string; ttl: number }; expect(body.challenge).toMatch(/^[0-9a-f]{64}$/); @@ -215,8 +215,8 @@ describe("POST /login", () => { it("rejects an event bound to another service (challenge-proxy phishing)", async () => { for (const relay of [ "wss://evil.example", - "wss://nostrbook.net.evil.example", - "https://evil.example/nostrbook.net", + "wss://nbread.lol.evil.example", + "https://evil.example/nbread.lol", "not a url at all", ]) { const challenge = await getChallenge(); @@ -228,7 +228,7 @@ describe("POST /login", () => { it("accepts an https:// relay binding for MAIN_HOST too", async () => { const challenge = await getChallenge(); const res = await postLogin( - signLoginEvent(challenge, { relay: "https://nostrbook.net" }), + signLoginEvent(challenge, { relay: "https://nbread.lol" }), ); expect(res.status).toBe(200); }); @@ -308,7 +308,7 @@ describe("sessions across requests + logout", () => { it("the session cookie works across multiple requests", async () => { const sid = await login(); for (let i = 0; i < 2; i++) { - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: `sid=${sid}` }, }); expect(res.status).toBe(200); @@ -317,12 +317,12 @@ describe("sessions across requests + logout", () => { }); it("anonymous / garbage cookies get redirected to /login", async () => { - const anon = await SELF.fetch("https://nostrbook.net/dashboard", { + const anon = await SELF.fetch("https://nbread.lol/dashboard", { redirect: "manual", }); expect(anon.status).toBe(302); expect(anon.headers.get("location")).toBe("/login"); - const garbage = await SELF.fetch("https://nostrbook.net/dashboard", { + const garbage = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: "sid=definitely-not-a-token" }, redirect: "manual", }); @@ -331,7 +331,7 @@ describe("sessions across requests + logout", () => { it("logout deletes the KV session and clears the cookie", async () => { const sid = await login(); - const res = await SELF.fetch("https://nostrbook.net/logout", { + const res = await SELF.fetch("https://nbread.lol/logout", { method: "POST", headers: { Cookie: `sid=${sid}` }, }); @@ -341,7 +341,7 @@ describe("sessions across requests + logout", () => { expect(cleared).toContain("Max-Age=0"); expect(await env.KV.get(sessionKey(sid))).toBeNull(); // The invalidated token no longer authenticates. - const after = await SELF.fetch("https://nostrbook.net/dashboard", { + const after = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: `sid=${sid}` }, redirect: "manual", }); @@ -363,12 +363,12 @@ describe("auth rate limits (D1 rate_limits)", () => { it("challenge: 10 per 15min per IP, 11th is 429", async () => { const ip = "192.0.2.12"; for (let i = 0; i < 10; i++) { - const res = await SELF.fetch("https://nostrbook.net/login/challenge", { + const res = await SELF.fetch("https://nbread.lol/login/challenge", { headers: { "CF-Connecting-IP": ip }, }); expect(res.status).toBe(200); } - const res = await SELF.fetch("https://nostrbook.net/login/challenge", { + const res = await SELF.fetch("https://nbread.lol/login/challenge", { headers: { "CF-Connecting-IP": ip }, }); expect(res.status).toBe(429); @@ -386,7 +386,7 @@ describe("auth rate limits (D1 rate_limits)", () => { ) .bind(CHALLENGE_GLOBAL_KEY, CHALLENGE_GLOBAL_DAILY_CAP, windowStart) .run(); - const res = await SELF.fetch("https://nostrbook.net/login/challenge", { + const res = await SELF.fetch("https://nbread.lol/login/challenge", { headers: { "CF-Connecting-IP": "192.0.2.99" }, }); expect(res.status).toBe(429); diff --git a/test/integration/cache.spec.ts b/test/integration/cache.spec.ts index d943db4..fc1dd1e 100644 --- a/test/integration/cache.spec.ts +++ b/test/integration/cache.spec.ts @@ -27,58 +27,58 @@ describe("tenant edge cache (Cache API + gen keys)", () => { it("misses cold, hits warm, with s-maxage=3600 on the cached response", async () => { await setGen(1000); - const r1 = await SELF.fetch("https://alice.nostrbook.net/"); + const r1 = await SELF.fetch("https://alice.nbread.lol/"); expect(r1.status).toBe(200); expect(r1.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expect(r1.headers.get("Cache-Control")).toContain("s-maxage=3600"); const body1 = await r1.text(); expect(body1).toContain("Hello world"); - const r2 = await SELF.fetch("https://alice.nostrbook.net/"); + const r2 = await SELF.fetch("https://alice.nbread.lol/"); expect(r2.headers.get(CACHE_STATUS_HEADER)).toBe("hit"); expect(await r2.text()).toBe(body1); }); it("caches per path (post page and home are separate entries)", async () => { await setGen(1100); - const post1 = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const post1 = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(post1.status).toBe(200); expect(post1.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); - const post2 = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const post2 = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(post2.headers.get(CACHE_STATUS_HEADER)).toBe("hit"); - const home = await SELF.fetch("https://alice.nostrbook.net/"); + const home = await SELF.fetch("https://alice.nbread.lol/"); expect(home.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); }); it("mirrorEvent's gen bump invalidates cached pages (new post appears)", async () => { await setGen(1200); - const r1 = await SELF.fetch("https://alice.nostrbook.net/"); + const r1 = await SELF.fetch("https://alice.nbread.lol/"); expect(r1.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expect(await r1.text()).not.toContain("Markdown torture test"); expect( - (await SELF.fetch("https://alice.nostrbook.net/")).headers.get( + (await SELF.fetch("https://alice.nbread.lol/")).headers.get( CACHE_STATUS_HEADER, ), ).toBe("hit"); expect(await mirrorEvent(env, aliceTorture)).toBe("stored"); // gen → new unique value - const r2 = await SELF.fetch("https://alice.nostrbook.net/"); + const r2 = await SELF.fetch("https://alice.nbread.lol/"); expect(r2.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expect(await r2.text()).toContain("Markdown torture test"); }); it("does not cache 404s", async () => { await setGen(1300); - const r1 = await SELF.fetch("https://alice.nostrbook.net/nope"); + const r1 = await SELF.fetch("https://alice.nbread.lol/nope"); expect(r1.status).toBe(404); expect(r1.headers.get(CACHE_STATUS_HEADER)).toBeNull(); - const r2 = await SELF.fetch("https://alice.nostrbook.net/nope"); + const r2 = await SELF.fetch("https://alice.nbread.lol/nope"); expect(r2.headers.get(CACHE_STATUS_HEADER)).toBeNull(); }); it("never caches apex (main site) responses", async () => { - const res = await SELF.fetch("https://nostrbook.net/"); + const res = await SELF.fetch("https://nbread.lol/"); expect(res.status).toBe(200); expect(res.headers.get(CACHE_STATUS_HEADER)).toBeNull(); expect(res.headers.get("Cache-Control")).toBeNull(); @@ -87,7 +87,7 @@ describe("tenant edge cache (Cache API + gen keys)", () => { // Mutates alice's posts — keep LAST in the file. it("keeps serving the cached page until the generation moves", async () => { await setGen(1400); - const r1 = await SELF.fetch("https://alice.nostrbook.net/"); + const r1 = await SELF.fetch("https://alice.nbread.lol/"); expect(r1.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expect(await r1.text()).toContain("Hello world"); @@ -98,13 +98,13 @@ describe("tenant edge cache (Cache API + gen keys)", () => { ) .bind(ALICE_PK) .run(); - const r2 = await SELF.fetch("https://alice.nostrbook.net/"); + const r2 = await SELF.fetch("https://alice.nbread.lol/"); expect(r2.headers.get(CACHE_STATUS_HEADER)).toBe("hit"); expect(await r2.text()).toContain("Hello world"); // Bump the generation → new key → fresh (empty) content. await setGen(1401); - const r3 = await SELF.fetch("https://alice.nostrbook.net/"); + const r3 = await SELF.fetch("https://alice.nbread.lol/"); expect(r3.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expect(await r3.text()).toContain("No posts yet."); }); diff --git a/test/integration/claim.spec.ts b/test/integration/claim.spec.ts index fd87177..d3f3167 100644 --- a/test/integration/claim.spec.ts +++ b/test/integration/claim.spec.ts @@ -26,7 +26,7 @@ function claim( handle: string, opts: { token?: string; ip?: string } = {}, ): Promise { - return SELF.fetch("https://nostrbook.net/dashboard/claim", { + return SELF.fetch("https://nbread.lol/dashboard/claim", { method: "POST", headers: { Cookie: `sid=${sid}`, @@ -66,7 +66,7 @@ afterEach(() => { describe("GET /dashboard", () => { it("shows the claim form (with Turnstile) before any claim", async () => { const sid = await createSession(env, ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: `sid=${sid}` }, }); expect(res.status).toBe(200); @@ -80,19 +80,19 @@ describe("GET /dashboard", () => { it("shows the handle (no claim form) once claimed", async () => { const sid = await createSession(env, ALICE_PK); expect((await claim(sid, "alice")).status).toBe(303); - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: `sid=${sid}` }, }); const html = await res.text(); - expect(html).toContain("alice.nostrbook.net"); - expect(html).toContain("alice@nostrbook.net"); + expect(html).toContain("alice.nbread.lol"); + expect(html).toContain("alice@nbread.lol"); expect(html).not.toContain("/dashboard/claim"); }); }); describe("POST /dashboard/claim", () => { it("requires a session", async () => { - const res = await SELF.fetch("https://nostrbook.net/dashboard/claim", { + const res = await SELF.fetch("https://nbread.lol/dashboard/claim", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: "handle=nobody&cf-turnstile-response=x", @@ -107,7 +107,7 @@ describe("POST /dashboard/claim", () => { expect(res.headers.get("location")).toBe("/dashboard"); expect(await handleOf(ALICE_PK)).toBe("alice"); // The blog is immediately live on its subdomain. - const blog = await SELF.fetch("https://alice.nostrbook.net/"); + const blog = await SELF.fetch("https://alice.nbread.lol/"); expect(blog.status).toBe(200); }); diff --git a/test/integration/csrf.spec.ts b/test/integration/csrf.spec.ts index 095db12..39157cf 100644 --- a/test/integration/csrf.spec.ts +++ b/test/integration/csrf.spec.ts @@ -11,7 +11,7 @@ function post( path: string, headers: Record = {}, ): Promise { - return SELF.fetch(`https://nostrbook.net${path}`, { + return SELF.fetch(`https://nbread.lol${path}`, { method: "POST", headers: { "Content-Type": "application/json", @@ -38,7 +38,7 @@ describe("csrf middleware (main site)", () => { }); it("rejects a subdomain Origin (tenant content must not drive the apex)", async () => { - const res = await post("/login", { Origin: "https://alice.nostrbook.net" }); + const res = await post("/login", { Origin: "https://alice.nbread.lol" }); expect(res.status).toBe(403); }); @@ -52,7 +52,7 @@ describe("csrf middleware (main site)", () => { it("accepts a same-origin POST (Origin + Sec-Fetch-Site)", async () => { // Passes CSRF, then fails JSON-body validation → 400, NOT 403. const res = await post("/login", { - Origin: "https://nostrbook.net", + Origin: "https://nbread.lol", "Sec-Fetch-Site": "same-origin", }); expect(res.status).toBe(400); @@ -64,7 +64,7 @@ describe("csrf middleware (main site)", () => { }); it("does not gate safe methods", async () => { - const res = await SELF.fetch("https://nostrbook.net/login", { + const res = await SELF.fetch("https://nbread.lol/login", { headers: { Origin: "https://evil.com" }, }); expect(res.status).toBe(200); // GET renders regardless of Origin @@ -73,7 +73,7 @@ describe("csrf middleware (main site)", () => { it("shields state-changing routes even with a valid session", async () => { const sid = await createSession(env, ALICE_PK); const forged = await SELF.fetch( - "https://nostrbook.net/dashboard/claim", + "https://nbread.lol/dashboard/claim", { method: "POST", headers: { diff --git a/test/integration/discover.spec.ts b/test/integration/discover.spec.ts index 614ded5..7143649 100644 --- a/test/integration/discover.spec.ts +++ b/test/integration/discover.spec.ts @@ -1,4 +1,4 @@ -// P6 discover feed (nostrbook.net/discover) + polished landing, end-to-end +// P6 discover feed (nbread.lol/discover) + polished landing, end-to-end // via SELF.fetch. Exercises the cross-tenant scoping trap: the events table // also holds unclaimed-npub mirrors and soft-deleted rows — none of those may // ever surface here. @@ -114,7 +114,7 @@ async function seedFlood(): Promise { } async function getDiscover(path = "/discover"): Promise { - return SELF.fetch(`https://nostrbook.net${path}`); + return SELF.fetch(`https://nbread.lol${path}`); } describe("discover feed (P6)", () => { @@ -138,10 +138,10 @@ describe("discover feed (P6)", () => { // created_at DESC: bobFirst (…400) > aliceXss (…300) > aliceTorture (…200) // > aliceHello (…100), asserted via each post's absolute blog URL. const order = [ - 'href="https://bob.nostrbook.net/bob-first"', - 'href="https://alice.nostrbook.net/xss-test"', - 'href="https://alice.nostrbook.net/markdown-torture"', - 'href="https://alice.nostrbook.net/hello-world"', + 'href="https://bob.nbread.lol/bob-first"', + 'href="https://alice.nbread.lol/xss-test"', + 'href="https://alice.nbread.lol/markdown-torture"', + 'href="https://alice.nbread.lol/hello-world"', ].map((needle) => { const at = html.indexOf(needle); expect(at, needle).toBeGreaterThan(-1); @@ -151,7 +151,7 @@ describe("discover feed (P6)", () => { // Author attribution links to the blog home. expect(html).toContain("@bob"); expect(html).toContain("@alice"); - expect(html).toContain('href="https://alice.nostrbook.net/"'); + expect(html).toContain('href="https://alice.nbread.lol/"'); // Titles/summaries come from tag strings as escaped text. expect(html).toContain("Markdown torture test"); expect(html).toContain("Every markdown feature in one post"); @@ -222,7 +222,7 @@ describe("discover feed (P6)", () => { .run(); // A cache HIT never touches the limiter — still 200 for this IP. - const hit = await SELF.fetch("https://nostrbook.net/discover", { + const hit = await SELF.fetch("https://nbread.lol/discover", { headers: { "CF-Connecting-IP": ip }, }); expect(hit.status).toBe(200); @@ -230,7 +230,7 @@ describe("discover feed (P6)", () => { // An uncached page (miss) is denied with a friendly 429 page. const denied = await SELF.fetch( - "https://nostrbook.net/discover?page=2", + "https://nbread.lol/discover?page=2", { headers: { "CF-Connecting-IP": ip } }, ); if (denied.status === 200 && attempt === 0) continue; // window flipped @@ -240,7 +240,7 @@ describe("discover feed (P6)", () => { expect(findXssVectors(html, "page")).toEqual([]); // The 429 was NOT cached: another client misses page 2 fresh (200). const other = await SELF.fetch( - "https://nostrbook.net/discover?page=2", + "https://nbread.lol/discover?page=2", { headers: { "CF-Connecting-IP": "203.0.113.98" } }, ); expect(other.status).toBe(200); @@ -338,7 +338,7 @@ describe("discover feed (P6)", () => { it("drops a post from the feed once its kind 5 delete is mirrored", async () => { let html = await (await getDiscover()).text(); - expect(html).toContain('href="https://alice.nostrbook.net/hello-world"'); + expect(html).toContain('href="https://alice.nbread.lol/hello-world"'); expect(await mirrorEvent(env, deleteHello, { bumpGen: false })).toBe( "stored", ); @@ -347,10 +347,10 @@ describe("discover feed (P6)", () => { await resetDiscoverCache(); html = await (await getDiscover()).text(); expect(html).not.toContain( - 'href="https://alice.nostrbook.net/hello-world"', + 'href="https://alice.nbread.lol/hello-world"', ); // The others stay. - expect(html).toContain('href="https://bob.nostrbook.net/bob-first"'); + expect(html).toContain('href="https://bob.nbread.lol/bob-first"'); }); }); @@ -380,7 +380,7 @@ describe("discover feed (P6)", () => { const page4 = await (await getDiscover("/discover?page=4")).text(); expect(page4).toContain("Flood post 05"); expect(page4).toContain("Flood post 01"); - expect(page4).toContain('href="https://alice.nostrbook.net/hello-world"'); + expect(page4).toContain('href="https://alice.nbread.lol/hello-world"'); expect(page4).not.toContain('rel="next"'); expect(page4).toContain('rel="prev"'); }); @@ -452,10 +452,10 @@ describe("discover feed (P6)", () => { describe("landing page (P6 polish)", () => { it("pitches the product and carries the login CTA", async () => { - const res = await SELF.fetch("https://nostrbook.net/"); + const res = await SELF.fetch("https://nbread.lol/"); expect(res.status).toBe(200); const html = await res.text(); - expect(html).toContain("Nostrbook"); + expect(html).toContain("nbread.lol"); expect(html).toContain('href="/login"'); expect(html).toContain('href="/discover"'); expect(html).toContain('href="/search"'); diff --git a/test/integration/editor.spec.ts b/test/integration/editor.spec.ts index eeb44f9..5f39c50 100644 --- a/test/integration/editor.spec.ts +++ b/test/integration/editor.spec.ts @@ -35,7 +35,7 @@ function postMirror( event: unknown, headers: Record = {}, ): Promise { - return SELF.fetch("https://nostrbook.net/api/mirror", { + return SELF.fetch("https://nbread.lol/api/mirror", { method: "POST", headers: { "Content-Type": "application/json", @@ -50,7 +50,7 @@ function postPreview( markdown: string, headers: Record = {}, ): Promise { - return SELF.fetch("https://nostrbook.net/dashboard/preview", { + return SELF.fetch("https://nbread.lol/dashboard/preview", { method: "POST", headers: { "Content-Type": "application/json", @@ -180,7 +180,7 @@ describe("POST /api/mirror — auth gates", () => { .bind(bobFirst.id) .first<{ deleted: number }>(); expect(row!.deleted).toBe(0); // bob's row untouched - const page = await SELF.fetch("https://bob.nostrbook.net/bob-first"); + const page = await SELF.fetch("https://bob.nbread.lol/bob-first"); expect(page.status).toBe(200); // and his blog still serves it }); }); @@ -192,7 +192,7 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { expect(res.status).toBe(200); expect(await res.json()).toEqual({ result: "stored" }); - const page = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const page = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(page.status).toBe(200); expect(await page.text()).toContain("Hello world"); }); @@ -203,7 +203,7 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { const edit = await postMirror(aliceHelloEdit, { Cookie: cookie }); expect(await edit.json()).toEqual({ result: "stored" }); - const home = await SELF.fetch("https://alice.nostrbook.net/"); + const home = await SELF.fetch("https://alice.nbread.lol/"); const html = await home.text(); expect(html).toContain("Hello world (edited)"); // The old version is GONE (replaceable slot), not listed alongside. @@ -227,7 +227,7 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { const cookie = await sessionCookieFor(ALICE_PK); await postMirror(aliceHello, { Cookie: cookie }); expect( - (await SELF.fetch("https://alice.nostrbook.net/hello-world")).status, + (await SELF.fetch("https://alice.nbread.lol/hello-world")).status, ).toBe(200); // Committed kind 5 fixture: e-tags aliceHello.id, a-tags its address — @@ -237,9 +237,9 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { expect(await res.json()).toEqual({ result: "stored" }); expect( - (await SELF.fetch("https://alice.nostrbook.net/hello-world")).status, + (await SELF.fetch("https://alice.nbread.lol/hello-world")).status, ).toBe(404); - const home = await SELF.fetch("https://alice.nostrbook.net/"); + const home = await SELF.fetch("https://alice.nbread.lol/"); expect(await home.text()).not.toContain("Hello world"); }); @@ -254,7 +254,7 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { const res = await postMirror(del, { Cookie: cookie }); expect(await res.json()).toEqual({ result: "stored" }); expect( - (await SELF.fetch("https://alice.nostrbook.net/hello-world")).status, + (await SELF.fetch("https://alice.nbread.lol/hello-world")).status, ).toBe(404); }); }); @@ -262,7 +262,7 @@ describe("POST /api/mirror — publish / edit / delete loop", () => { describe("editor pages", () => { it("redirects anonymous users to /login", async () => { for (const path of ["/dashboard/posts/new", "/dashboard/editor?slug=hello-world"]) { - const res = await SELF.fetch(`https://nostrbook.net${path}`, { + const res = await SELF.fetch(`https://nbread.lol${path}`, { redirect: "manual", }); expect(res.status, path).toBe(302); @@ -272,7 +272,7 @@ describe("editor pages", () => { it("serves the blank editor at /dashboard/posts/new", async () => { const cookie = await sessionCookieFor(ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard/posts/new", { + const res = await SELF.fetch("https://nbread.lol/dashboard/posts/new", { headers: { Cookie: cookie }, }); expect(res.status).toBe(200); @@ -287,7 +287,7 @@ describe("editor pages", () => { await mirrorEvent(env, aliceHello); const cookie = await sessionCookieFor(ALICE_PK); const res = await SELF.fetch( - "https://nostrbook.net/dashboard/editor?slug=hello-world", + "https://nbread.lol/dashboard/editor?slug=hello-world", { headers: { Cookie: cookie } }, ); expect(res.status).toBe(200); @@ -303,7 +303,7 @@ describe("editor pages", () => { const cookie = await sessionCookieFor(ALICE_PK); for (const slug of ["no-such-post", "bob-first"]) { const res = await SELF.fetch( - `https://nostrbook.net/dashboard/editor?slug=${slug}`, + `https://nbread.lol/dashboard/editor?slug=${slug}`, { headers: { Cookie: cookie } }, ); expect(res.status, slug).toBe(404); @@ -314,7 +314,7 @@ describe("editor pages", () => { await mirrorEvent(env, aliceXss); const cookie = await sessionCookieFor(ALICE_PK); const res = await SELF.fetch( - "https://nostrbook.net/dashboard/editor?slug=xss-test", + "https://nbread.lol/dashboard/editor?slug=xss-test", { headers: { Cookie: cookie } }, ); expect(res.status).toBe(200); @@ -333,7 +333,7 @@ describe("POST /dashboard/preview — parity with the publish pipeline", () => { it("rejects a non-string body shape (400)", async () => { const cookie = await sessionCookieFor(ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard/preview", { + const res = await SELF.fetch("https://nbread.lol/dashboard/preview", { method: "POST", headers: { "Content-Type": "application/json", Cookie: cookie }, body: JSON.stringify({ markdown: 42 }), @@ -406,7 +406,7 @@ describe("POST /api/mirror — review-fix hardening", () => { controller.close(); }, }); - const res = await SELF.fetch("https://nostrbook.net/api/mirror", { + const res = await SELF.fetch("https://nbread.lol/api/mirror", { method: "POST", headers: { "Content-Type": "application/json", @@ -503,7 +503,7 @@ describe("POST /api/mirror — review-fix hardening", () => { .first<{ deleted: number }>(); expect(row?.deleted).toBe(1); expect( - (await SELF.fetch("https://alice.nostrbook.net/hello-world")).status, + (await SELF.fetch("https://alice.nbread.lol/hello-world")).status, ).toBe(404); }); }); @@ -520,7 +520,7 @@ describe("editor pages — review-fix hardening", () => { expect((await postMirror(post, { Cookie: cookie })).status).toBe(200); const res = await SELF.fetch( - "https://nostrbook.net/dashboard/editor?slug=new", + "https://nbread.lol/dashboard/editor?slug=new", { headers: { Cookie: cookie } }, ); expect(res.status).toBe(200); @@ -529,7 +529,7 @@ describe("editor pages — review-fix hardening", () => { expect(html).toContain("the slug is new"); // The literal new-post page is untouched by the route move. - const blank = await SELF.fetch("https://nostrbook.net/dashboard/posts/new", { + const blank = await SELF.fetch("https://nbread.lol/dashboard/posts/new", { headers: { Cookie: cookie }, }); expect(blank.status).toBe(200); @@ -546,7 +546,7 @@ describe("editor pages — review-fix hardening", () => { await mirrorEvent(env, post); const cookie = await sessionCookieFor(ALICE_PK); const res = await SELF.fetch( - "https://nostrbook.net/dashboard/editor?slug=leading-newline", + "https://nbread.lol/dashboard/editor?slug=leading-newline", { headers: { Cookie: cookie } }, ); const html = await res.text(); @@ -582,7 +582,7 @@ describe("editor page — toolbar, tabs, and script wiring", () => { async function fetchEditorHtml(): Promise { const cookie = await sessionCookieFor(ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard/posts/new", { + const res = await SELF.fetch("https://nbread.lol/dashboard/posts/new", { headers: { Cookie: cookie }, }); expect(res.status).toBe(200); diff --git a/test/integration/headers.spec.ts b/test/integration/headers.spec.ts index 4e40fc5..db41f4e 100644 --- a/test/integration/headers.spec.ts +++ b/test/integration/headers.spec.ts @@ -73,13 +73,13 @@ describe("isNpubPath (host-class selection)", () => { describe("apex class", () => { it("landing page carries the exact apex CSP + XFO DENY", async () => { - const res = await SELF.fetch("https://nostrbook.net/"); + const res = await SELF.fetch("https://nbread.lol/"); expect(res.status).toBe(200); expectApexClass(res); }); it("login page (ships JS) still fits inside the apex CSP", async () => { - const res = await SELF.fetch("https://nostrbook.net/login"); + const res = await SELF.fetch("https://nbread.lol/login"); expect(res.status).toBe(200); expectApexClass(res); // The CSP must actually permit what the page uses: same-origin scripts. @@ -92,7 +92,7 @@ describe("apex class", () => { }); it("redirects carry headers too (anonymous /dashboard → /login)", async () => { - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { redirect: "manual", }); expect(res.status).toBe(302); @@ -100,14 +100,14 @@ describe("apex class", () => { }); it("apex 404s carry headers", async () => { - const res = await SELF.fetch("https://nostrbook.net/no-such-page"); + const res = await SELF.fetch("https://nbread.lol/no-such-page"); expect(res.status).toBe(404); expectApexClass(res); }); it("nostr.json keeps its CORS + content type under the apex class", async () => { const res = await SELF.fetch( - "https://nostrbook.net/.well-known/nostr.json?name=alice", + "https://nbread.lol/.well-known/nostr.json?name=alice", ); expect(res.status).toBe(200); expectApexClass(res); @@ -116,24 +116,24 @@ describe("apex class", () => { }); it("unknown host class (guard 404) gets the strict apex default", async () => { - const res = await SELF.fetch("https://nostrbook.net.evil.com/"); + const res = await SELF.fetch("https://nbread.lol.evil.com/"); expect(res.status).toBe(404); expectApexClass(res); }); it("unclaimed subdomain (tenant 404) gets the strict apex default", async () => { - const res = await SELF.fetch("https://unknown.nostrbook.net/"); + const res = await SELF.fetch("https://unknown.nbread.lol/"); expect(res.status).toBe(404); expectApexClass(res); }); it("/discover carries apex headers on miss AND on cache hit", async () => { - const miss = await SELF.fetch("https://nostrbook.net/discover"); + const miss = await SELF.fetch("https://nbread.lol/discover"); expect(miss.status).toBe(200); expect(miss.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expectApexClass(miss); - const hit = await SELF.fetch("https://nostrbook.net/discover"); + const hit = await SELF.fetch("https://nbread.lol/discover"); expect(hit.status).toBe(200); expect(hit.headers.get(CACHE_STATUS_HEADER)).toBe("hit"); expectApexClass(hit); @@ -142,21 +142,21 @@ describe("apex class", () => { describe("blog class (tenant subdomains)", () => { it("blog home carries the exact blog CSP on miss AND on cache hit", async () => { - const miss = await SELF.fetch("https://alice.nostrbook.net/"); + const miss = await SELF.fetch("https://alice.nbread.lol/"); expect(miss.status).toBe(200); expect(miss.headers.get(CACHE_STATUS_HEADER)).toBe("miss"); expectBlogClass(miss); - const hit = await SELF.fetch("https://alice.nostrbook.net/"); + const hit = await SELF.fetch("https://alice.nbread.lol/"); expect(hit.status).toBe(200); expect(hit.headers.get(CACHE_STATUS_HEADER)).toBe("hit"); expectBlogClass(hit); }); it("blog pages serve NO JavaScript (CSP forbids it; markup ships none)", async () => { - const home = await SELF.fetch("https://alice.nostrbook.net/"); + const home = await SELF.fetch("https://alice.nbread.lol/"); expect((await home.text()).toLowerCase()).not.toContain(" { }); it("RSS keeps its XML content type + blog-class headers", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/rss.xml"); + const res = await SELF.fetch("https://alice.nbread.lol/rss.xml"); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toBe( "application/rss+xml; charset=utf-8", @@ -175,21 +175,21 @@ describe("blog class (tenant subdomains)", () => { }); it("sitemap + robots keep their content types + headers", async () => { - const sitemap = await SELF.fetch("https://alice.nostrbook.net/sitemap.xml"); + const sitemap = await SELF.fetch("https://alice.nbread.lol/sitemap.xml"); expect(sitemap.status).toBe(200); expect(sitemap.headers.get("Content-Type")).toBe( "application/xml; charset=utf-8", ); expectBlogClass(sitemap); - const robots = await SELF.fetch("https://alice.nostrbook.net/robots.txt"); + const robots = await SELF.fetch("https://alice.nbread.lol/robots.txt"); expect(robots.status).toBe(200); expect(robots.headers.get("Content-Type")).toContain("text/plain"); expectBlogClass(robots); }); it("blog 404s (unknown slug) carry blog-class headers", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/no-such-post"); + const res = await SELF.fetch("https://alice.nbread.lol/no-such-post"); expect(res.status).toBe(404); expectBlogClass(res); }); @@ -203,7 +203,7 @@ describe("blog class (tenant subdomains)", () => { // …and blog markup legitimately ships neither element, so the pins are // free: home + post pages contain no
    /. for (const path of ["/", "/hello-world"]) { - const res = await SELF.fetch(`https://alice.nostrbook.net${path}`); + const res = await SELF.fetch(`https://alice.nbread.lol${path}`); const html = (await res.text()).toLowerCase(); expect(html).not.toContain(" { const bobNpub = npubEncode(BOB_PK); it("npub home renders with blog-class headers (no XFO)", async () => { - const res = await SELF.fetch(`https://nostrbook.net/${bobNpub}`); + const res = await SELF.fetch(`https://nbread.lol/${bobNpub}`); expect(res.status).toBe(200); expectBlogClass(res); expect((await res.text()).toLowerCase()).not.toContain(" { - const res = await SELF.fetch(`https://nostrbook.net/${bobNpub}/rss.xml`); + const res = await SELF.fetch(`https://nbread.lol/${bobNpub}/rss.xml`); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toBe( "application/rss+xml; charset=utf-8", @@ -232,7 +232,7 @@ describe("blog class (apex /npub1… views)", () => { it("shape-valid npub with a bad checksum 404s with blog-class headers", async () => { const zeds = "z".repeat(58); - const res = await SELF.fetch(`https://nostrbook.net/npub1${zeds}`); + const res = await SELF.fetch(`https://nbread.lol/npub1${zeds}`); expect(res.status).toBe(404); expectBlogClass(res); }); diff --git a/test/integration/info.spec.ts b/test/integration/info.spec.ts index 696373d..7685032 100644 --- a/test/integration/info.spec.ts +++ b/test/integration/info.spec.ts @@ -13,7 +13,7 @@ const INFO_PAGES = [ describe("info pages (/privacy /terms /docs)", () => { for (const { path, needle } of INFO_PAGES) { it(`${path} serves cacheable static HTML with the shared chrome`, async () => { - const res = await SELF.fetch(`https://nostrbook.net${path}`); + const res = await SELF.fetch(`https://nbread.lol${path}`); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toContain("text/html"); expect(res.headers.get("Cache-Control")).toBe("public, max-age=3600"); @@ -30,14 +30,14 @@ describe("info pages (/privacy /terms /docs)", () => { it("every info page links the AGPL source repo", async () => { for (const { path } of INFO_PAGES) { - const res = await SELF.fetch(`https://nostrbook.net${path}`); + const res = await SELF.fetch(`https://nbread.lol${path}`); const html = await res.text(); - expect(html, path).toContain("github.com/sovITxyz/nostrbook"); + expect(html, path).toContain("github.com/sovITxyz/nbread"); } // The license itself is named where it matters. - const privacy = await SELF.fetch("https://nostrbook.net/privacy"); + const privacy = await SELF.fetch("https://nbread.lol/privacy"); expect(await privacy.text()).toContain("AGPL"); - const docs = await SELF.fetch("https://nostrbook.net/docs"); + const docs = await SELF.fetch("https://nbread.lol/docs"); expect(await docs.text()).toContain("AGPL"); }); }); @@ -45,7 +45,7 @@ describe("info pages (/privacy /terms /docs)", () => { describe("apex 404", () => { it("an unknown apex path renders the HTML 404 page", async () => { const res = await SELF.fetch( - "https://nostrbook.net/definitely-not-a-page", + "https://nbread.lol/definitely-not-a-page", ); expect(res.status).toBe(404); expect(res.headers.get("Content-Type")).toContain("text/html"); @@ -58,7 +58,7 @@ describe("apex 404", () => { it("an unknown /api path stays a plain-text 404 (no markup)", async () => { const res = await SELF.fetch( - "https://nostrbook.net/api/definitely-not-a-page", + "https://nbread.lol/api/definitely-not-a-page", ); expect(res.status).toBe(404); expect(res.headers.get("Content-Type")).not.toContain("text/html"); diff --git a/test/integration/ingestion.spec.ts b/test/integration/ingestion.spec.ts index 820f9a7..9fdba68 100644 --- a/test/integration/ingestion.spec.ts +++ b/test/integration/ingestion.spec.ts @@ -25,7 +25,7 @@ describe("public blog from mirrored events (SELF.fetch, D1 provider)", () => { }); it("lists mirrored posts on the blog home with the mirrored profile", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/"); + const res = await SELF.fetch("https://alice.nbread.lol/"); expect(res.status).toBe(200); const html = await res.text(); expect(html).toContain("Hello world"); @@ -38,7 +38,7 @@ describe("public blog from mirrored events (SELF.fetch, D1 provider)", () => { it("serves the post page from STORED html with zero renderPost calls", async () => { const before = getRenderPostCallCount(); - const res = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const res = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(res.status).toBe(200); const html = await res.text(); expect(html).toContain("alice"); @@ -48,7 +48,7 @@ describe("public blog from mirrored events (SELF.fetch, D1 provider)", () => { }); it("neutralizes the XSS post end-to-end through D1", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/xss-test"); + const res = await SELF.fetch("https://alice.nbread.lol/xss-test"); expect(res.status).toBe(200); const html = await res.text(); expect(findXssVectors(html, "page")).toEqual([]); @@ -56,18 +56,18 @@ describe("public blog from mirrored events (SELF.fetch, D1 provider)", () => { }); it("serves valid RSS built from mirrored events", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/rss.xml"); + const res = await SELF.fetch("https://alice.nbread.lol/rss.xml"); expect(res.status).toBe(200); const xml = await res.text(); expect(XMLValidator.validate(xml)).toBe(true); expect(xml).toContain("alice-test"); expect(xml).toContain( - "https://alice.nostrbook.net/hello-world", + "https://alice.nbread.lol/hello-world", ); }); it("404s a slug that is not mirrored", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/no-such-post"); + const res = await SELF.fetch("https://alice.nbread.lol/no-such-post"); expect(res.status).toBe(404); }); @@ -75,16 +75,16 @@ describe("public blog from mirrored events (SELF.fetch, D1 provider)", () => { expect(await mirrorEvent(env, deleteByAlice)).toBe("stored"); const home = await ( - await SELF.fetch("https://alice.nostrbook.net/") + await SELF.fetch("https://alice.nbread.lol/") ).text(); expect(home).not.toContain("Hello world"); expect(home).toContain("Markdown torture test"); // others still listed - const post = await SELF.fetch("https://alice.nostrbook.net/hello-world"); + const post = await SELF.fetch("https://alice.nbread.lol/hello-world"); expect(post.status).toBe(404); const rss = await ( - await SELF.fetch("https://alice.nostrbook.net/rss.xml") + await SELF.fetch("https://alice.nbread.lol/rss.xml") ).text(); expect(rss).not.toContain("hello-world"); }); diff --git a/test/integration/mirror.spec.ts b/test/integration/mirror.spec.ts index 12e85ad..f7b6834 100644 --- a/test/integration/mirror.spec.ts +++ b/test/integration/mirror.spec.ts @@ -112,7 +112,7 @@ describe("mirrorEvent — store + render-at-ingest", () => { expect(rows[0]!.rendered).toBeNull(); // only 30023 rows are rendered const profile = await getProfile(env, aliceProfile.pubkey); expect(profile?.name).toBe("alice-test"); - expect(profile?.nip05).toBe("alice@nostrbook.net"); + expect(profile?.nip05).toBe("alice@nbread.lol"); }); }); diff --git a/test/integration/npub-budget.spec.ts b/test/integration/npub-budget.spec.ts index 69796d6..d8bef74 100644 --- a/test/integration/npub-budget.spec.ts +++ b/test/integration/npub-budget.spec.ts @@ -87,14 +87,14 @@ describe("npub on-demand mirror budget", () => { await seedCounter("npub-mirror:ip:9.9.9.9", NPUB_MIRROR_IP_DAILY_CAP); // The capped IP gets a page but NO relay mirror session runs: - const r1 = await SELF.fetch(`https://nostrbook.net/${bobNpub}`, { + const r1 = await SELF.fetch(`https://nbread.lol/${bobNpub}`, { headers: { "CF-Connecting-IP": "9.9.9.9" }, }); expect(r1.status).toBe(200); expect(await eventCount(BOB_PK)).toBe(0); // A different IP still has budget (the denial did NOT set the cooldown): - const r2 = await SELF.fetch(`https://nostrbook.net/${bobNpub}`, { + const r2 = await SELF.fetch(`https://nbread.lol/${bobNpub}`, { headers: { "CF-Connecting-IP": "8.8.8.8" }, }); expect(r2.status).toBe(200); @@ -105,14 +105,14 @@ describe("npub on-demand mirror budget", () => { serveEvents(aliceEvents); await seedCounter(NPUB_MIRROR_GLOBAL_KEY, NPUB_MIRROR_GLOBAL_DAILY_CAP); - const r1 = await SELF.fetch(`https://nostrbook.net/${aliceNpub}`); + const r1 = await SELF.fetch(`https://nbread.lol/${aliceNpub}`); expect(r1.status).toBe(200); // serves what D1 has (nothing) without relay work expect(await eventCount(ALICE_PK)).toBe(0); // Budget frees up (next day) → the same npub mirrors normally, because // the denied attempt never marked the cooldown: await env.DB.prepare("DELETE FROM rate_limits").run(); - const r2 = await SELF.fetch(`https://nostrbook.net/${aliceNpub}`); + const r2 = await SELF.fetch(`https://nbread.lol/${aliceNpub}`); expect(r2.status).toBe(200); expect(await eventCount(ALICE_PK)).toBe(aliceEvents.length); }); diff --git a/test/integration/npub.spec.ts b/test/integration/npub.spec.ts index cdf5473..7e4da7d 100644 --- a/test/integration/npub.spec.ts +++ b/test/integration/npub.spec.ts @@ -1,4 +1,4 @@ -// nostrbook.net/npub1… — on-demand mirror for unclaimed pubkeys: newest-10 +// nbread.lol/npub1… — on-demand mirror for unclaimed pubkeys: newest-10 // cap per request, progressive backfill via ctx.waitUntil, claimed pubkeys // redirect to their subdomain, malformed/blocked npubs 404. import { SELF, env } from "cloudflare:test"; @@ -37,40 +37,40 @@ afterEach(async () => { await defaultCache().delete(npubCooldownKey(BOB_PK)); }); -describe("nostrbook.net/npub1… (on-demand mirror)", () => { +describe("nbread.lol/npub1… (on-demand mirror)", () => { it("404s a shape-valid npub with a bad checksum without touching relays", async () => { const res = await SELF.fetch( - `https://nostrbook.net/npub1${"z".repeat(58)}`, + `https://nbread.lol/npub1${"z".repeat(58)}`, ); expect(res.status).toBe(404); }); it("404s non-npub garbage paths", async () => { - const res = await SELF.fetch("https://nostrbook.net/not-an-npub"); + const res = await SELF.fetch("https://nbread.lol/not-an-npub"); expect(res.status).toBe(404); }); it("redirects a claimed pubkey's npub to its subdomain", async () => { await seedAlice(); - const res = await SELF.fetch(`https://nostrbook.net/${aliceNpub}`, { + const res = await SELF.fetch(`https://nbread.lol/${aliceNpub}`, { redirect: "manual", }); expect(res.status).toBe(302); - expect(res.headers.get("location")).toBe("https://alice.nostrbook.net/"); + expect(res.headers.get("location")).toBe("https://alice.nbread.lol/"); const post = await SELF.fetch( - `https://nostrbook.net/${aliceNpub}/hello-world`, + `https://nbread.lol/${aliceNpub}/hello-world`, { redirect: "manual" }, ); expect(post.status).toBe(302); expect(post.headers.get("location")).toBe( - "https://alice.nostrbook.net/hello-world", + "https://alice.nbread.lol/hello-world", ); }); it("404s a blocked pubkey's npub", async () => { await seedBlockedMallory(); - const res = await SELF.fetch(`https://nostrbook.net/${malloryNpub}`); + const res = await SELF.fetch(`https://nbread.lol/${malloryNpub}`); expect(res.status).toBe(404); }); @@ -78,7 +78,7 @@ describe("nostrbook.net/npub1… (on-demand mirror)", () => { serveEvents(bobEvents); // First visit: synchronous fetch+mirror, capped at the newest 10. - const r1 = await SELF.fetch(`https://nostrbook.net/${bobNpub}`); + const r1 = await SELF.fetch(`https://nbread.lol/${bobNpub}`); expect(r1.status).toBe(200); const html1 = await r1.text(); expect(html1).toContain("Bulk post 12"); @@ -88,7 +88,7 @@ describe("nostrbook.net/npub1… (on-demand mirror)", () => { // Second visit (cooldown cleared): served from D1 immediately, backfill // runs via ctx.waitUntil and mirrors the remaining 4 events. await defaultCache().delete(npubCooldownKey(BOB_PK)); - const r2 = await SELF.fetch(`https://nostrbook.net/${bobNpub}`); + const r2 = await SELF.fetch(`https://nbread.lol/${bobNpub}`); expect(r2.status).toBe(200); await vi.waitFor(async () => { expect(await bobCount()).toBe(bobEvents.length); // all 14 @@ -97,7 +97,7 @@ describe("nostrbook.net/npub1… (on-demand mirror)", () => { // With the profile now mirrored, the header shows bob's kind 0 name. await defaultCache().delete(npubCooldownKey(BOB_PK)); const html3 = await ( - await SELF.fetch(`https://nostrbook.net/${bobNpub}`) + await SELF.fetch(`https://nbread.lol/${bobNpub}`) ).text(); expect(html3).toContain("bob-test"); expect(html3).toContain("Bob's first post"); @@ -105,10 +105,10 @@ describe("nostrbook.net/npub1… (on-demand mirror)", () => { it("serves a mirrored post page under the npub base path", async () => { serveEvents(bobEvents); - await SELF.fetch(`https://nostrbook.net/${bobNpub}`); // seeds newest 10 + await SELF.fetch(`https://nbread.lol/${bobNpub}`); // seeds newest 10 const res = await SELF.fetch( - `https://nostrbook.net/${bobNpub}/bulk-12`, + `https://nbread.lol/${bobNpub}/bulk-12`, ); expect(res.status).toBe(200); const html = await res.text(); @@ -116,22 +116,22 @@ describe("nostrbook.net/npub1… (on-demand mirror)", () => { expect(html).toContain("Bulk body 12."); const missing = await SELF.fetch( - `https://nostrbook.net/${bobNpub}/no-such-slug`, + `https://nbread.lol/${bobNpub}/no-such-slug`, ); expect(missing.status).toBe(404); }); it("serves valid RSS under the npub base path", async () => { serveEvents(bobEvents); - await SELF.fetch(`https://nostrbook.net/${bobNpub}`); + await SELF.fetch(`https://nbread.lol/${bobNpub}`); const res = await SELF.fetch( - `https://nostrbook.net/${bobNpub}/rss.xml`, + `https://nbread.lol/${bobNpub}/rss.xml`, ); expect(res.status).toBe(200); expect(res.headers.get("content-type")).toContain("application/rss+xml"); const xml = await res.text(); expect(xml).toContain( - `https://nostrbook.net/${bobNpub}/bulk-12`, + `https://nbread.lol/${bobNpub}/bulk-12`, ); }); }); diff --git a/test/integration/ratelimit-gaps.spec.ts b/test/integration/ratelimit-gaps.spec.ts index e16774c..59e7ded 100644 --- a/test/integration/ratelimit-gaps.spec.ts +++ b/test/integration/ratelimit-gaps.spec.ts @@ -51,12 +51,12 @@ describe("npub view limiter", () => { NPUB_VIEW_RATE_MAX, NPUB_VIEW_RATE_WINDOW_SECONDS, ); - const denied = await SELF.fetch(`https://nostrbook.net/${badNpub}`, { + const denied = await SELF.fetch(`https://nbread.lol/${badNpub}`, { headers: { "CF-Connecting-IP": "6.6.6.6" }, }); expect(denied.status).toBe(429); - const other = await SELF.fetch(`https://nostrbook.net/${badNpub}`, { + const other = await SELF.fetch(`https://nbread.lol/${badNpub}`, { headers: { "CF-Connecting-IP": "7.7.7.7" }, }); expect(other.status).toBe(404); // normal not-found, not rate limited @@ -69,7 +69,7 @@ describe("npub view limiter", () => { NPUB_VIEW_RATE_WINDOW_SECONDS, ); const denied = await SELF.fetch( - `https://nostrbook.net/${badNpub}/rss.xml`, + `https://nbread.lol/${badNpub}/rss.xml`, { headers: { "CF-Connecting-IP": "6.6.6.7" } }, ); expect(denied.status).toBe(429); @@ -81,7 +81,7 @@ describe("logout limiter", () => { const token = await createSession(env, ALICE_PK); await seedCounter("logout:ip:6.6.7.1", LOGOUT_MAX, 15 * 60); - const denied = await SELF.fetch("https://nostrbook.net/logout", { + const denied = await SELF.fetch("https://nbread.lol/logout", { method: "POST", headers: { "CF-Connecting-IP": "6.6.7.1", Cookie: `sid=${token}` }, }); @@ -90,7 +90,7 @@ describe("logout limiter", () => { expect(await env.KV.get(sessionKey(token))).not.toBeNull(); // A fresh IP logs out normally and the session dies. - const ok = await SELF.fetch("https://nostrbook.net/logout", { + const ok = await SELF.fetch("https://nbread.lol/logout", { method: "POST", headers: { "CF-Connecting-IP": "6.6.7.2", Cookie: `sid=${token}` }, }); @@ -105,19 +105,19 @@ describe("dashboard view limiter", () => { const bobCookie = await sessionCookieFor(BOB_PK); await seedCounter(`dash:pk:${ALICE_PK}`, DASHBOARD_VIEW_MAX, 5 * 60); - const denied = await SELF.fetch("https://nostrbook.net/dashboard", { + const denied = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: aliceCookie }, }); expect(denied.status).toBe(429); - const ok = await SELF.fetch("https://nostrbook.net/dashboard", { + const ok = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: bobCookie }, }); expect(ok.status).toBe(200); }); it("anonymous dashboard requests still redirect (no limiter spend)", async () => { - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { redirect: "manual", }); expect(res.status).toBe(302); diff --git a/test/integration/search.spec.ts b/test/integration/search.spec.ts index 3169045..f9be27d 100644 --- a/test/integration/search.spec.ts +++ b/test/integration/search.spec.ts @@ -1,4 +1,4 @@ -// P6 search (nostrbook.net/search + services/search) end-to-end via +// P6 search (nbread.lol/search + services/search) end-to-end via // SELF.fetch and direct service calls: canned queries over fixtures, the // MATCH injection corpus (never a 5xx), strict echo escaping, discover-equal // scoping, and the per-IP rate limit. @@ -38,7 +38,7 @@ let ipSeq = 0; /** GET /search with a fresh client IP so tests never trip each other's window. */ async function getSearch(q?: string, ip?: string): Promise { const query = q === undefined ? "" : `?q=${encodeURIComponent(q)}`; - return SELF.fetch(`https://nostrbook.net/search${query}`, { + return SELF.fetch(`https://nbread.lol/search${query}`, { headers: { "CF-Connecting-IP": ip ?? `10.6.0.${++ipSeq % 250}` }, }); } @@ -138,7 +138,7 @@ describe("search (P6)", () => { const res = await getSearch("hello"); expect(res.status).toBe(200); const html = await res.text(); - expect(html).toContain('href="https://alice.nostrbook.net/hello-world"'); + expect(html).toContain('href="https://alice.nbread.lol/hello-world"'); expect(html).toContain("Hello world"); expect(html).not.toContain("bob-first"); expect(html).toMatchSnapshot(); diff --git a/test/integration/settings.spec.ts b/test/integration/settings.spec.ts index 9def239..38c5e88 100644 --- a/test/integration/settings.spec.ts +++ b/test/integration/settings.spec.ts @@ -26,7 +26,7 @@ function postSettings( fields: Record, headers: Record = {}, ): Promise { - return SELF.fetch("https://nostrbook.net/dashboard/settings", { + return SELF.fetch("https://nbread.lol/dashboard/settings", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", @@ -178,7 +178,7 @@ describe("POST /dashboard/settings", () => { { css: "body { background: #fffdf5 }", about: "", relays: "" }, { Cookie: cookie }, ); - const page = await SELF.fetch("https://alice.nostrbook.net/"); + const page = await SELF.fetch("https://alice.nbread.lol/"); const html = await page.text(); expect(html).toContain("background: #fffdf5"); }); @@ -188,7 +188,7 @@ describe("GET /dashboard — post list + settings form", () => { it("shows the user's posts with edit links, the new-post button, and the settings form", async () => { await mirrorEvent(env, aliceHello); const cookie = await sessionCookieFor(ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: cookie }, }); expect(res.status).toBe(200); @@ -205,7 +205,7 @@ describe("GET /dashboard — post list + settings form", () => { it("does not list other users' posts", async () => { await mirrorEvent(env, fixtures.posts.bobFirst as NostrEvent); const cookie = await sessionCookieFor(ALICE_PK); - const res = await SELF.fetch("https://nostrbook.net/dashboard", { + const res = await SELF.fetch("https://nbread.lol/dashboard", { headers: { Cookie: cookie }, }); const html = await res.text(); @@ -219,7 +219,7 @@ describe("GET /dashboard — post list + settings form", () => { { css: "body { color: teal }", about: "hello about", relays: "wss://nos.lol" }, { Cookie: cookie }, ); - const res = await SELF.fetch("https://nostrbook.net/dashboard?saved=1", { + const res = await SELF.fetch("https://nbread.lol/dashboard?saved=1", { headers: { Cookie: cookie }, }); const html = await res.text(); @@ -302,13 +302,13 @@ describe("POST /dashboard/settings — review-fix hardening", () => { ); for (const path of ["/", "/hello-world"]) { - const page = await SELF.fetch(`https://alice.nostrbook.net${path}`); + const page = await SELF.fetch(`https://alice.nbread.lol${path}`); expect(page.status, path).toBe(200); const html = await page.text(); expect(html, path).toContain("about from settings"); // The kind-0 bio is REPLACED, not shown alongside. expect(html, path).not.toContain( - "Nostrbook throwaway test profile (alice)", + "nbread.lol throwaway test profile (alice)", ); } }); diff --git a/test/integration/tenant.spec.ts b/test/integration/tenant.spec.ts index cc49bc7..85a560d 100644 --- a/test/integration/tenant.spec.ts +++ b/test/integration/tenant.spec.ts @@ -78,12 +78,12 @@ describe("blog subdomain rendering (SELF.fetch)", () => { describe("blog home", () => { it("lists posts with title, summary and date", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/"); + const res = await SELF.fetch("https://alice.nbread.lol/"); expect(res.status).toBe(200); const html = await res.text(); expect(html).toContain("Hello world"); expect(html).toContain("Markdown torture test"); - expect(html).toContain("Alice's first Nostrbook test post"); + expect(html).toContain("Alice's first nbread.lol test post"); expect(html).toContain("2023-11-14"); expect(html).toContain('href="/hello-world"'); expect(html).toMatchSnapshot(); @@ -91,17 +91,17 @@ describe("blog subdomain rendering (SELF.fetch)", () => { it("shows the profile header (name, picture, about) and @handle", async () => { const html = await ( - await SELF.fetch("https://alice.nostrbook.net/") + await SELF.fetch("https://alice.nbread.lol/") ).text(); expect(html).toContain("alice-test"); expect(html).toContain('src="https://example.com/alice.png"'); - expect(html).toContain("Nostrbook throwaway test profile (alice)"); + expect(html).toContain("nbread.lol throwaway test profile (alice)"); expect(html).toContain("@alice"); }); it("inlines the theme CSS only after sanitization", async () => { const html = await ( - await SELF.fetch("https://alice.nostrbook.net/") + await SELF.fetch("https://alice.nbread.lol/") ).text(); expect(html).toContain("color: #345"); expect(html).not.toMatch(/@import/i); @@ -111,7 +111,7 @@ describe("blog subdomain rendering (SELF.fetch)", () => { it("escapes hostile post titles/summaries in the list (attribute + text)", async () => { const html = await ( - await SELF.fetch("https://alice.nostrbook.net/") + await SELF.fetch("https://alice.nbread.lol/") ).text(); expect(findXssVectors(html, "page")).toEqual([]); }); @@ -120,7 +120,7 @@ describe("blog subdomain rendering (SELF.fetch)", () => { describe("post page", () => { it("renders title, date and content", async () => { const res = await SELF.fetch( - "https://alice.nostrbook.net/hello-world", + "https://alice.nbread.lol/hello-world", ); expect(res.status).toBe(200); const html = await res.text(); @@ -132,7 +132,7 @@ describe("blog subdomain rendering (SELF.fetch)", () => { it("renders the torture post with all features", async () => { const html = await ( - await SELF.fetch("https://alice.nostrbook.net/markdown-torture") + await SELF.fetch("https://alice.nbread.lol/markdown-torture") ).text(); expect(html).toContain('class="hljs language-js"'); expect(html).toContain('
    '); @@ -141,7 +141,7 @@ describe("blog subdomain rendering (SELF.fetch)", () => { }); it("neutralizes every vector in the XSS post (content, title, summary)", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/xss-test"); + const res = await SELF.fetch("https://alice.nbread.lol/xss-test"); expect(res.status).toBe(200); const html = await res.text(); expect(findXssVectors(html, "page")).toEqual([]); @@ -152,34 +152,34 @@ describe("blog subdomain rendering (SELF.fetch)", () => { }); it("404s an unknown slug with the rendered not-found page", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/no-such-post"); + const res = await SELF.fetch("https://alice.nbread.lol/no-such-post"); expect(res.status).toBe(404); expect(await res.text()).toContain("404"); }); it("404s deep paths", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/a/b/c"); + const res = await SELF.fetch("https://alice.nbread.lol/a/b/c"); expect(res.status).toBe(404); }); }); describe("feeds and crawler files", () => { it("serves well-formed RSS with escaped titles", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/rss.xml"); + const res = await SELF.fetch("https://alice.nbread.lol/rss.xml"); expect(res.status).toBe(200); expect(res.headers.get("content-type")).toContain("application/rss+xml"); const xml = await res.text(); expect(XMLValidator.validate(xml)).toBe(true); expect(xml).toContain("alice-test"); expect(xml).toContain( - "https://alice.nostrbook.net/hello-world", + "https://alice.nbread.lol/hello-world", ); expect(xml.toLowerCase()).not.toContain(" { - const res = await SELF.fetch("https://alice.nostrbook.net/atom.xml"); + const res = await SELF.fetch("https://alice.nbread.lol/atom.xml"); expect(res.status).toBe(200); expect(res.headers.get("content-type")).toContain( "application/atom+xml", @@ -191,24 +191,24 @@ describe("blog subdomain rendering (SELF.fetch)", () => { }); it("serves a well-formed sitemap.xml", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/sitemap.xml"); + const res = await SELF.fetch("https://alice.nbread.lol/sitemap.xml"); expect(res.status).toBe(200); expect(res.headers.get("content-type")).toContain("application/xml"); const xml = await res.text(); expect(XMLValidator.validate(xml)).toBe(true); expect(xml).toContain( - "https://alice.nostrbook.net/hello-world", + "https://alice.nbread.lol/hello-world", ); expect(xml).toMatchSnapshot(); }); it("serves robots.txt pointing at the sitemap", async () => { - const res = await SELF.fetch("https://alice.nostrbook.net/robots.txt"); + const res = await SELF.fetch("https://alice.nbread.lol/robots.txt"); expect(res.status).toBe(200); const body = await res.text(); expect(body).toContain("User-agent: *"); expect(body).toContain( - "Sitemap: https://alice.nostrbook.net/sitemap.xml", + "Sitemap: https://alice.nbread.lol/sitemap.xml", ); }); }); @@ -228,21 +228,21 @@ describe("blog subdomain rendering (SELF.fetch)", () => { }); try { const home = await ( - await SELF.fetch("https://alice.nostrbook.net/") + await SELF.fetch("https://alice.nbread.lol/") ).text(); expect(home).not.toContain("Ghost post"); expect(home).toContain("Hello world"); // real posts still listed const rss = await ( - await SELF.fetch("https://alice.nostrbook.net/rss.xml") + await SELF.fetch("https://alice.nbread.lol/rss.xml") ).text(); expect(rss).not.toContain("Ghost post"); const sitemap = await ( - await SELF.fetch("https://alice.nostrbook.net/sitemap.xml") + await SELF.fetch("https://alice.nbread.lol/sitemap.xml") ).text(); const homeLocs = sitemap.match( - /https:\/\/alice\.nostrbook\.net\/<\/loc>/g, + /https:\/\/alice\.nbread\.lol\/<\/loc>/g, ); expect(homeLocs?.length ?? 0).toBe(1); // home emitted exactly once } finally { @@ -260,14 +260,14 @@ describe("blog subdomain rendering (SELF.fetch)", () => { .bind(fixtures.profiles.bob.pubkey, "bob", "2026-01-01T00:00:00.000Z") .run(); - const home = await SELF.fetch("https://bob.nostrbook.net/"); + const home = await SELF.fetch("https://bob.nbread.lol/"); expect(home.status).toBe(200); expect(await home.text()).toContain("No posts yet."); - const rss = await SELF.fetch("https://bob.nostrbook.net/rss.xml"); + const rss = await SELF.fetch("https://bob.nbread.lol/rss.xml"); expect(XMLValidator.validate(await rss.text())).toBe(true); - const sitemap = await SELF.fetch("https://bob.nostrbook.net/sitemap.xml"); + const sitemap = await SELF.fetch("https://bob.nbread.lol/sitemap.xml"); expect(XMLValidator.validate(await sitemap.text())).toBe(true); }); }); diff --git a/test/integration/wellknown.spec.ts b/test/integration/wellknown.spec.ts index 510cf4f..5d9a522 100644 --- a/test/integration/wellknown.spec.ts +++ b/test/integration/wellknown.spec.ts @@ -11,7 +11,7 @@ type Nip05Body = { async function nip05(query: string): Promise<{ res: Response; body: Nip05Body }> { const res = await SELF.fetch( - `https://nostrbook.net/.well-known/nostr.json${query}`, + `https://nbread.lol/.well-known/nostr.json${query}`, ); const body = (await res.json()) as Nip05Body; return { res, body }; @@ -62,7 +62,7 @@ describe("/.well-known/nostr.json (NIP-05)", () => { it("is apex-only (blog subdomains 404)", async () => { const res = await SELF.fetch( - "https://alice.nostrbook.net/.well-known/nostr.json?name=alice", + "https://alice.nbread.lol/.well-known/nostr.json?name=alice", ); expect(res.status).toBe(404); }); diff --git a/test/unit/editor-md.spec.ts b/test/unit/editor-md.spec.ts index f199c1c..84dc1c1 100644 --- a/test/unit/editor-md.spec.ts +++ b/test/unit/editor-md.spec.ts @@ -1,6 +1,6 @@ // Redesign: the DOM-free markdown text-manipulation core behind the editor // toolbar (public/js/editor-md.js). The IIFE is imported for its side effect -// (it assigns globalThis.NostrbookEditorMd) and exercised directly — every +// (it assigns globalThis.NbreadEditorMd) and exercised directly — every // returned instruction also goes through apply(), which asserts the // byte-identity property: splicing `text` over [start, end) must leave every // byte outside that range untouched. @@ -18,7 +18,7 @@ type Instr = { }; /* eslint-disable @typescript-eslint/no-explicit-any */ -const md = (globalThis as any).NostrbookEditorMd as { +const md = (globalThis as any).NbreadEditorMd as { wrapInline: (v: string, s: number, e: number, marker: string) => Instr | null; toggleLinePrefix: (v: string, s: number, e: number, prefix: string) => Instr; cycleHeading: (v: string, s: number, e: number) => Instr; diff --git a/test/unit/nip23.spec.ts b/test/unit/nip23.spec.ts index 8fa7836..38bcb05 100644 --- a/test/unit/nip23.spec.ts +++ b/test/unit/nip23.spec.ts @@ -25,7 +25,7 @@ describe("postMeta", () => { expect(meta).toEqual({ slug: "hello-world", title: "Hello world", - summary: "Alice's first Nostrbook test post", + summary: "Alice's first nbread.lol test post", published_at: 1700000100, image: null, }); diff --git a/test/unit/tenant.spec.ts b/test/unit/tenant.spec.ts index b2b48eb..b2429ca 100644 --- a/test/unit/tenant.spec.ts +++ b/test/unit/tenant.spec.ts @@ -1,8 +1,8 @@ // Tenant + guard middleware unit tests — all 4 host classes: -// 1. nostrbook.net → main site -// 2. alice.nostrbook.net → blog site (claimed handle) -// 3. unknown.nostrbook.net → 404 (unclaimed handle) -// 4. nostrbook.net.evil.com → 404 (spoofed host class) +// 1. nbread.lol → main site +// 2. alice.nbread.lol → blog site (claimed handle) +// 3. unknown.nbread.lol → 404 (unclaimed handle) +// 4. nbread.lol.evil.com → 404 (spoofed host class) import { env } from "cloudflare:test"; import { beforeAll, describe, expect, it } from "vitest"; import { Hono } from "hono"; @@ -32,15 +32,15 @@ describe("guard + tenant middleware host classes", () => { await seedBlockedMallory(); }); - it("1. apex (nostrbook.net) resolves to the main site", async () => { - const res = await probeApp().request(req("nostrbook.net"), undefined, env); + it("1. apex (nbread.lol) resolves to the main site", async () => { + const res = await probeApp().request(req("nbread.lol"), undefined, env); expect(res.status).toBe(200); expect(await res.json()).toEqual({ type: "main" }); }); - it("2. claimed subdomain (alice.nostrbook.net) resolves to the blog site", async () => { + it("2. claimed subdomain (alice.nbread.lol) resolves to the blog site", async () => { const res = await probeApp().request( - req("alice.nostrbook.net"), + req("alice.nbread.lol"), undefined, env, ); @@ -55,27 +55,27 @@ describe("guard + tenant middleware host classes", () => { expect(site.user.handle).toBe("alice"); }); - it("3. unclaimed subdomain (unknown.nostrbook.net) is a 404", async () => { + it("3. unclaimed subdomain (unknown.nbread.lol) is a 404", async () => { const res = await probeApp().request( - req("unknown.nostrbook.net"), + req("unknown.nbread.lol"), undefined, env, ); expect(res.status).toBe(404); }); - it("4. spoofed host class (nostrbook.net.evil.com) is a 404", async () => { + it("4. spoofed host class (nbread.lol.evil.com) is a 404", async () => { const res = await probeApp().request( - req("nostrbook.net.evil.com"), + req("nbread.lol.evil.com"), undefined, env, ); expect(res.status).toBe(404); }); - it("rejects deep subdomains (a.b.nostrbook.net)", async () => { + it("rejects deep subdomains (a.b.nbread.lol)", async () => { const res = await probeApp().request( - req("a.b.nostrbook.net"), + req("a.b.nbread.lol"), undefined, env, ); @@ -89,7 +89,7 @@ describe("guard + tenant middleware host classes", () => { it("blocked users are 404 on their subdomain", async () => { const res = await probeApp().request( - req("blocked.nostrbook.net"), + req("blocked.nbread.lol"), undefined, env, ); @@ -98,7 +98,7 @@ describe("guard + tenant middleware host classes", () => { it("host matching is case-insensitive and ignores ports", async () => { const res = await probeApp().request( - req("ALICE.NostrBook.NET:8443"), + req("ALICE.NbRead.LOL:8443"), undefined, env, ); @@ -113,7 +113,7 @@ describe("guard + tenant middleware host classes", () => { const r = new Request("http://localhost:8787/", { headers: { host: "localhost:8787", - "x-forwarded-host": "alice.nostrbook.net", + "x-forwarded-host": "alice.nbread.lol", }, }); const res = await probeApp().request(r, undefined, env); @@ -125,10 +125,10 @@ describe("guard + tenant middleware host classes", () => { it("X-Forwarded-Host is IGNORED outside development", async () => { // Simulate the deployed config (wrangler.jsonc ships ENVIRONMENT=production). const prodEnv = { ...env, ENVIRONMENT: "production" }; - const r = new Request("https://nostrbook.net/", { + const r = new Request("https://nbread.lol/", { headers: { - host: "nostrbook.net", - "x-forwarded-host": "alice.nostrbook.net", + host: "nbread.lol", + "x-forwarded-host": "alice.nbread.lol", }, }); const res = await probeApp().request(r, undefined, prodEnv); @@ -153,23 +153,23 @@ describe("guard hostname hardening", () => { }); const res = await guardApp().request(r, undefined, env); expect(res.status).toBe(200); - expect(await res.text()).toBe("nostrbook.net"); + expect(await res.text()).toBe("nbread.lol"); }); it("accepts a maximal 63-char DNS label", async () => { const label = "a".repeat(63); const res = await guardApp().request( - req(`${label}.nostrbook.net`), + req(`${label}.nbread.lol`), undefined, env, ); expect(res.status).toBe(200); - expect(await res.text()).toBe(`${label}.nostrbook.net`); + expect(await res.text()).toBe(`${label}.nbread.lol`); }); it("rejects labels longer than 63 chars", async () => { const res = await guardApp().request( - req(`${"a".repeat(64)}.nostrbook.net`), + req(`${"a".repeat(64)}.nbread.lol`), undefined, env, ); @@ -178,14 +178,14 @@ describe("guard hostname hardening", () => { it("rejects hostnames longer than 253 chars", async () => { // 4 × 62-char labels + suffix ≈ 266 chars, each label individually valid. - const long = Array(4).fill("a".repeat(62)).join(".") + ".nostrbook.net"; + const long = Array(4).fill("a".repeat(62)).join(".") + ".nbread.lol"; const res = await guardApp().request(req(long), undefined, env); expect(res.status).toBe(404); }); it("rejects labels containing underscores", async () => { const res = await guardApp().request( - req("a_b.nostrbook.net"), + req("a_b.nbread.lol"), undefined, env, ); @@ -193,7 +193,7 @@ describe("guard hostname hardening", () => { }); it("rejects labels with leading or trailing hyphens", async () => { - for (const host of ["-alice.nostrbook.net", "alice-.nostrbook.net"]) { + for (const host of ["-alice.nbread.lol", "alice-.nbread.lol"]) { const res = await guardApp().request(req(host), undefined, env); expect(res.status, host).toBe(404); } @@ -201,7 +201,7 @@ describe("guard hostname hardening", () => { it("accepts interior hyphens (alice-blog)", async () => { const res = await guardApp().request( - req("alice-blog.nostrbook.net"), + req("alice-blog.nbread.lol"), undefined, env, ); @@ -211,8 +211,8 @@ describe("guard hostname hardening", () => { it("rejects hosts containing whitespace", async () => { // Space must ride in the Host header only — a URL with a space in the // host would throw in the Request constructor itself. - const r = new Request("https://nostrbook.net/", { - headers: { host: "ali ce.nostrbook.net" }, + const r = new Request("https://nbread.lol/", { + headers: { host: "ali ce.nbread.lol" }, }); const res = await guardApp().request(r, undefined, env); expect(res.status).toBe(404); @@ -221,8 +221,8 @@ describe("guard hostname hardening", () => { describe("normalizeHostname (direct — bytes Headers cannot carry)", () => { it("strips ports and lowercases", () => { - expect(normalizeHostname("ALICE.NostrBook.NET:8443")).toBe( - "alice.nostrbook.net", + expect(normalizeHostname("ALICE.NbRead.LOL:8443")).toBe( + "alice.nbread.lol", ); }); @@ -231,15 +231,15 @@ describe("normalizeHostname (direct — bytes Headers cannot carry)", () => { }); it("returns null for NUL bytes", () => { - expect(normalizeHostname("ali\u0000ce.nostrbook.net")).toBeNull(); + expect(normalizeHostname("ali\u0000ce.nbread.lol")).toBeNull(); }); it("returns null for spaces", () => { - expect(normalizeHostname("ali ce.nostrbook.net")).toBeNull(); + expect(normalizeHostname("ali ce.nbread.lol")).toBeNull(); }); it("returns null for empty or oversized hostnames", () => { expect(normalizeHostname("")).toBeNull(); - expect(normalizeHostname("a".repeat(300) + ".nostrbook.net")).toBeNull(); + expect(normalizeHostname("a".repeat(300) + ".nbread.lol")).toBeNull(); }); }); diff --git a/test/unit/xml.spec.ts b/test/unit/xml.spec.ts index a3b2938..1493d5d 100644 --- a/test/unit/xml.spec.ts +++ b/test/unit/xml.spec.ts @@ -18,7 +18,7 @@ const dTagless: NostrEvent = { const opts = { title: "alice-test", description: "test blog", - baseUrl: "https://alice.nostrbook.net", + baseUrl: "https://alice.nbread.lol", handle: "alice", posts: [hello, dTagless], }; @@ -27,9 +27,9 @@ describe("d-tagless posts are excluded from feeds and sitemap", () => { it("rssFeed lists only addressable posts", () => { const xml = rssFeed(opts); expect(XMLValidator.validate(xml)).toBe(true); - expect(xml).toContain("https://alice.nostrbook.net/hello-world"); + expect(xml).toContain("https://alice.nbread.lol/hello-world"); // no item link collapsing onto the blog home - expect(xml).not.toContain("https://alice.nostrbook.net/\nhttps://alice.nbread.lol/\n/g)?.length ?? 0).toBe(1); }); @@ -37,17 +37,17 @@ describe("d-tagless posts are excluded from feeds and sitemap", () => { const xml = atomFeed(opts); expect(XMLValidator.validate(xml)).toBe(true); expect(xml.match(//g)?.length ?? 0).toBe(1); - expect(xml).toContain("https://alice.nostrbook.net/hello-world"); + expect(xml).toContain("https://alice.nbread.lol/hello-world"); }); it("sitemapXml never emits the home twice", () => { const xml = sitemapXml({ baseUrl: opts.baseUrl, posts: opts.posts }); expect(XMLValidator.validate(xml)).toBe(true); const homeLocs = xml.match( - /https:\/\/alice\.nostrbook\.net\/<\/loc>/g, + /https:\/\/alice\.nbread\.lol\/<\/loc>/g, ); expect(homeLocs?.length ?? 0).toBe(1); - expect(xml).toContain("https://alice.nostrbook.net/hello-world"); + expect(xml).toContain("https://alice.nbread.lol/hello-world"); }); it("feeds with ONLY a d-tagless post degrade to valid empty feeds", () => { diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index e20a859..c1ab9b3 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,13 +1,13 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: 3368267aacc27b8854825a930fdb5040) +// Generated by Wrangler by running `wrangler types` (hash: 3d63818717005fdab6ba2d5dc2ddb507) // Runtime types generated with workerd@1.20260708.1 2026-01-01 interface __BaseEnv_Env { KV: KVNamespace; DB: D1Database; ASSETS: Fetcher; - MAIN_HOST: "nostrbook.net"; - TURNSTILE_SITE_KEY: "1x00000000000000000000AA"; - RELAYS: "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band"; + MAIN_HOST: "nbread.lol"; + TURNSTILE_SITE_KEY: "0x4AAAAAAD1JzzXDBykpiavq"; + RELAYS: "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band,wss://relay.primal.net,wss://nostr.wine,wss://purplepag.es,wss://relay.snort.social,wss://nostr.mom"; ENVIRONMENT: string; TURNSTILE_SECRET_KEY: string; } diff --git a/wrangler.jsonc b/wrangler.jsonc index 2bb2f29..6c5d0b3 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -1,8 +1,8 @@ -// Nostrbook — Cloudflare Worker configuration +// nbread.lol — Cloudflare Worker configuration // One Worker + Static Assets (NOT Pages). Free tier: 10ms CPU/request budget. { "$schema": "node_modules/wrangler/config-schema.json", - "name": "nostrbook", + "name": "nbread", "main": "src/index.ts", "compatibility_date": "2026-01-01", "assets": { @@ -12,7 +12,7 @@ "d1_databases": [ { "binding": "DB", - "database_name": "nostrbook", + "database_name": "nbread", // Sovereign IT account (region ENAM); created 2026-07-13 per docs/setup.md. // Local dev + tests use miniflare bindings and ignore this id. "database_id": "e9163b12-3cde-4b82-961b-b825c85321e3", @@ -27,17 +27,20 @@ } ], "vars": { - "MAIN_HOST": "nostrbook.net", + "MAIN_HOST": "nbread.lol", // Fail closed: the committed config is production-safe. Local dev gets // ENVIRONMENT=development via .dev.vars (wrangler dev) and the miniflare // bindings override in vitest.config.ts (tests/CI). Never set this to // "development" here — it enables the client-spoofable X-Forwarded-Host // override in src/middleware/guard.ts. "ENVIRONMENT": "production", - // Real Turnstile widget (Sovereign IT → "nostrbook", Managed) for - // nostrbook.net + the workers.dev preview host. Secret is a wrangler secret. + // Real Turnstile widget (Sovereign IT → "nbread", Managed) for + // nbread.lol + the workers.dev preview host. Secret is a wrangler secret. "TURNSTILE_SITE_KEY": "0x4AAAAAAD1JzzXDBykpiavq", - "RELAYS": "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band" + // Default relays: the service-wide fallback used for cron sync + NIP-05 + // relay hints when a user has not set their own list (dashboard settings + // override these, up to MAX_RELAYS). Comma-separated wss:// URLs. + "RELAYS": "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band,wss://relay.primal.net,wss://nostr.wine,wss://purplepag.es,wss://relay.snort.social,wss://nostr.mom" }, // Secrets (set via `wrangler secret put`, never committed): // TURNSTILE_SECRET_KEY — Turnstile siteverify secret (required in prod) @@ -50,11 +53,11 @@ // Gate A preview target (docs/ops.md). Post-launch it is harmless — the // host guard 404s every request whose Host is not MAIN_HOST or a // single-label subdomain of it, and the committed MAIN_HOST is - // nostrbook.net (Gate A previews override it per-deploy with --var). + // nbread.lol (Gate A previews override it per-deploy with --var). "workers_dev": true, "routes": [ - { "pattern": "nostrbook.net/*", "zone_name": "nostrbook.net" }, - { "pattern": "*.nostrbook.net/*", "zone_name": "nostrbook.net" } + { "pattern": "nbread.lol/*", "zone_name": "nbread.lol" }, + { "pattern": "*.nbread.lol/*", "zone_name": "nbread.lol" } ], "observability": { "enabled": true