diff --git a/AGENTS.md b/AGENTS.md index bba6760..f710135 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -605,8 +605,9 @@ session with **no other memory of this project**. So: announcements on a **heartbeat** (`CVM_HEARTBEAT_MS`, default 15 min) so the owner Settings health card (stale after 45 min) stays green; `LOG_LEVEL` is now wired to the transport. Relays come from `NOSTR_RELAYS` in the gitignored - `cvm-server/.env` — policy is **single community relay** - (`wss://goskatespots.nostr1.com`). The tracked manifest is `package.cvm.json`. Tools: + `cvm-server/.env` — policy is **community relay** (self-hosted + `wss://relay.skate-spots.org` since 2026-10-12, with the legacy hosted relay as + a fallback). The tracked manifest is `package.cvm.json`. Tools: `find_spots` / `find_events` / `find_coaches` (all Lab kinds 35001–35014) / `find_gear` / `find_crews` / `find_shops` / `find_tours` + `get_community_stats` — kept at parity with the app's `useCVMClient`. Has a @@ -2017,7 +2018,7 @@ Rules: relay, signatures intact). Default dir `~/.backups/relay` (override with `SKATESPOTS_ARCHIVE_DIR`), keeps the last 12, and writes `manifest-*.json` + `latest.json` (counts by kind, author count, sha256, byte size). -- ⚠️ **`goskatespots.nostr1.com` is a PUBLIC relay with general traffic** +- ⚠️ **The legacy hosted relay `goskatespots.nostr1.com` is PUBLIC with general traffic** (verified 2026-09-28): 15.7k kind-0 profiles, mostly non-skaters; 2k kind-30078 app-data (Armada/Plebs/Amethyst); 379 kind-30023 articles (Yakihonne); 408 NIP-99 listings (Shopstr). So the archive is deliberately **scoped** — exclusive @@ -2064,6 +2065,42 @@ Rules: - Commits touching only `scripts/`, `*.md`, `cvm-server/`, `skills/`, `.agents/` are **skipped by the deploy timer** → no Netlify credits spent. +### Community relay — self-hosted Newlay (2026-10-12) + +The community relay moved off the hosted relay.tools instance +(`wss://goskatespots.nostr1.com`) to **our own Newlay** at +**`wss://relay.skate-spots.org`**. Host: the same VPS as `ai.`/`push.` +(`185.18.221.2`, `vm-1705.lnvps.cloud`, ssh key `~/.ssh/skatespots-new`). + +- **Stack:** Newlay (`artifacts.relay.tools/opensauce/newlay`, MIT) in **Docker** + (Compose), bound to **`127.0.0.1:7777` only**; **Caddy** terminates TLS (a + `relay.skate-spots.org` site block was **appended** to `/etc/caddy/Caddyfile`, + auto Let's Encrypt) and reverse-proxies it. Do NOT add nginx — Caddy is already + the proxy for ai/push/relay. +- **Repo on the box:** `~/newlay`; config `~/newlay/deploy/docker/newlay.toml` + (bind-mounted read-only into the container). `sudo docker compose up -d`. +- **Config choices:** `[tier.anon]` open read+write (no WoT gate, no spider, no + Blossom); `created_at_msecs_ago = 315360000000` (10 years — the default 365-day + window silently dropped an old spot on restore); publishing/filter limits raised + from the tight defaults. The relay is keyless (no NIP-43/NIP-29 membership). +- **Data:** restored from the archive — **115/115 events, 17 authors**, every kind + matching. LMDB + (unused) Blossom live in the Docker volume + `newlay_newlay-data`. +- **NIP-86:** Newlay advertises it, but admin rights must be configured relay-side; + until then `src/lib/relayAdmin.ts` (`banevent`) 401s and the app falls back to + NIP-32 label hiding. +- **Cutover (2026-10-12):** `SKATE_RELAY` (`src/lib/types.ts`) → the new relay; the + default pools, the DM/CVM/zap relay arrays, the `cvm-server` + `push-service` + defaults, and the archive default all point at the new relay **with the legacy + relay kept as a fallback**. ⚠️ **Retire the legacy relay only after old native + clients update** — the web app is instant (Netlify), the APK lags (Zapstore). + Until then the old relay still receives writes so old clients can read. +- ⚠️ `relay.skate-spots.org` can show a stale NXDOMAIN in a local resolver cache + right after creation (our dev box did). Verify with + `curl --resolve relay.skate-spots.org:443:185.18.221.2 …` or from another host. +- **Backups:** `scripts/archive-relay.mjs` now defaults to the new relay; the + weekly timer + restic offsite are unchanged. + ### Git / ngit publishing - Push: `git push origin main` (handled by `git-remote-nostr`; publishes a signed repo-state event). - Login (once): `ngit account login --interactive` → scan the QR in Amber ("Add a new application → Connect app"). Plain `ngit account login` errors; the `--interactive` flag is required. diff --git a/BOUNTY-SYSTEM-PLAN.md b/BOUNTY-SYSTEM-PLAN.md index f4f3b12..593cf59 100644 --- a/BOUNTY-SYSTEM-PLAN.md +++ b/BOUNTY-SYSTEM-PLAN.md @@ -127,7 +127,7 @@ if (distance > bountyRegion.radiusKm) { **What:** Only Pyramid relay members can claim bounties **How it works:** -- Bounty claims only valid from `goskatespots.nostr1.com` members +- Bounty claims only valid from `relay.skate-spots.org` members - Invite-only relay (using Pyramid) - Members vouch for quality - Spam = member gets kicked diff --git a/FOLLOW_MANAGEMENT_BRIEFING.md b/FOLLOW_MANAGEMENT_BRIEFING.md index 315ab62..8343765 100644 --- a/FOLLOW_MANAGEMENT_BRIEFING.md +++ b/FOLLOW_MANAGEMENT_BRIEFING.md @@ -321,7 +321,7 @@ A: Following builds your Web of Trust network, which helps you: **Where to find skaters:** - Search `#skateboarding` on primal.net - Browse skate-related posts on Damus -- Check who's active on `wss://goskatespots.nostr1.com` +- Check who's active on `wss://relay.skate-spots.org` --- diff --git a/MCP_SERVER_SPEC.md b/MCP_SERVER_SPEC.md index f50c7bb..f10c32d 100644 --- a/MCP_SERVER_SPEC.md +++ b/MCP_SERVER_SPEC.md @@ -416,7 +416,7 @@ await server.connect(transport); import { NPool, NRelay1 } from '@nostrify/nostrify'; const RELAYS = [ - 'wss://goskatespots.nostr1.com', + 'wss://relay.skate-spots.org', 'wss://wotr.relatr.xyz', ]; @@ -481,7 +481,7 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' const signer = new PrivateKeySigner(process.env.NOSTR_PRIVATE_KEY!); const relayPool = new SimpleRelayPool([ - 'wss://goskatespots.nostr1.com', + 'wss://relay.skate-spots.org', 'wss://wotr.relatr.xyz', ]); diff --git a/README.md b/README.md index a5f1e9e..93f2f32 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ This app uses custom Nostr event kinds defined in `NIP.md`: ### Relay Configuration -Default relay: `wss://goskatespots.nostr1.com` +Default relay: `wss://relay.skate-spots.org` Users can manage relays in Settings following NIP-65. diff --git a/cvm-server/.env.example b/cvm-server/.env.example index 3f6bdd2..fb53491 100644 --- a/cvm-server/.env.example +++ b/cvm-server/.env.example @@ -9,7 +9,7 @@ CVM_SERVER_NSEC_HEX=your_64_char_hex_private_key_here # Nostr relay(s) to query for skate data (comma-separated). # This deployment uses only the community relay — the canonical SkateSpots data. -NOSTR_RELAYS=wss://goskatespots.nostr1.com +NOSTR_RELAYS=wss://relay.skate-spots.org # CVM discovery relay — where agents find your server CVM_RELAY=wss://relay.contextvm.org diff --git a/cvm-server/DEPLOY.md b/cvm-server/DEPLOY.md index b34cb5f..3c57066 100644 --- a/cvm-server/DEPLOY.md +++ b/cvm-server/DEPLOY.md @@ -88,7 +88,7 @@ nano .env Fill in: ```env CVM_SERVER_NSEC_HEX=PASTE_YOUR_HEX_KEY_FROM_STEP_3_HERE -NOSTR_RELAYS=wss://goskatespots.nostr1.com,wss://relay.ditto.pub +NOSTR_RELAYS=wss://relay.skate-spots.org,wss://relay.ditto.pub CVM_RELAY=wss://relay.contextvm.org LOG_LEVEL=info ``` @@ -108,7 +108,7 @@ You should see: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🛹 SkateSpots CVM Server ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Data relays : wss://goskatespots.nostr1.com, ... + Data relays : wss://relay.skate-spots.org, ... Server npub : abc123... ✅ Server running — listening for agents on Nostr Verify discovery: diff --git a/cvm-server/server.ts b/cvm-server/server.ts index 27787b1..4ec70ef 100644 --- a/cvm-server/server.ts +++ b/cvm-server/server.ts @@ -37,7 +37,7 @@ if (!NSEC_HEX) { process.exit(1); } -const NOSTR_RELAYS = (process.env.NOSTR_RELAYS || "wss://goskatespots.nostr1.com") +const NOSTR_RELAYS = (process.env.NOSTR_RELAYS || "wss://relay.skate-spots.org,wss://goskatespots.nostr1.com") .split(",") .map((r) => r.trim()) .filter(Boolean); diff --git a/push-service/.env.example b/push-service/.env.example index af6c279..eb9aa6c 100644 --- a/push-service/.env.example +++ b/push-service/.env.example @@ -11,7 +11,7 @@ PUSH_VAPID_PUBLIC_KEY= PUSH_VAPID_PRIVATE_KEY= # Relays to watch for push-marked kind-1059 wraps (comma-separated). -PUSH_RELAYS=wss://goskatespots.nostr1.com +PUSH_RELAYS=wss://relay.skate-spots.org # The plaintext push marker tag name on recipient wraps: ["", "push"]. PUSH_MARKER=skatespots diff --git a/push-service/README.md b/push-service/README.md index 3cc63a0..14d5545 100644 --- a/push-service/README.md +++ b/push-service/README.md @@ -63,7 +63,7 @@ See `.env.example`. Key ones: ```bash cd push-service bun install -PORT=8788 PUSH_RELAYS=wss://goskatespots.nostr1.com bun run server.ts +PORT=8788 PUSH_RELAYS=wss://relay.skate-spots.org bun run server.ts # typecheck (from repo root): npx tsc -p push-service/tsconfig.json ``` diff --git a/push-service/server.ts b/push-service/server.ts index 6a13f9a..6c76640 100644 --- a/push-service/server.ts +++ b/push-service/server.ts @@ -36,7 +36,7 @@ const env = process.env; const PORT = Number(env.PORT ?? 8788); const MARKER = (env.PUSH_MARKER ?? 'skatespots').trim(); const STORAGE_PATH = env.PUSH_STORAGE_PATH ?? './push-state.json'; -const RELAYS = (env.PUSH_RELAYS ?? 'wss://goskatespots.nostr1.com') +const RELAYS = (env.PUSH_RELAYS ?? 'wss://relay.skate-spots.org,wss://goskatespots.nostr1.com') .split(',').map((s) => s.trim()).filter(Boolean); const LOOKBACK_SECONDS = Number(env.PUSH_LOOKBACK_SECONDS ?? 3 * 24 * 60 * 60); const CHALLENGE_TTL_SECONDS = Number(env.PUSH_CHALLENGE_TTL_SECONDS ?? 5 * 60); diff --git a/scripts/archive-relay.mjs b/scripts/archive-relay.mjs index 9f068ff..6725872 100755 --- a/scripts/archive-relay.mjs +++ b/scripts/archive-relay.mjs @@ -9,12 +9,13 @@ // // node scripts/archive-relay.mjs [--out DIR] [--relay URL] [--keep N] // -// ⚠️ goskatespots.nostr1.com is a PUBLIC relay carrying general Nostr traffic -// (verified 2026-09-28): 15.7k kind-0 profiles (mostly non-skaters), 2k kind-30078 -// app-data events (Armada/Plebs/Amethyst), 379 long-form articles (Yakihonne), -// 408 NIP-99 listings (Shopstr)… almost none of it ours. Archiving the whole -// relay would mean taking custody of strangers' data AND republishing it on a -// restore. So this stays deliberately scoped to the app's own content: +// The community relay is now our own Newlay instance (relay.skate-spots.org, +// 2026-10-12), which carries only app content. The LEGACY hosted relay +// (goskatespots.nostr1.com) was PUBLIC with general Nostr traffic (verified +// 2026-09-28): 15.7k kind-0 profiles, Armada/Plebs/Amethyst app-data, Yakihonne +// articles, Shopstr listings… almost none of it ours. Archiving wholesale would +// mean taking custody of strangers' data AND republishing it on a restore, so +// this stays deliberately scoped to the app's own content either way: // // 1. exclusive kinds the app owns outright (30818 spots, 30017 shops, …); // 2. shared kinds filtered by the app's OWN tags (e.g. `t=skateboarding` @@ -36,7 +37,7 @@ import { join } from 'node:path'; import { homedir } from 'node:os'; import { SimplePool, verifyEvent } from 'nostr-tools'; -const DEFAULT_RELAY = 'wss://goskatespots.nostr1.com'; +const DEFAULT_RELAY = 'wss://relay.skate-spots.org'; const PAGE = 2000; // small pages: a big page can close early and come back short const MAX_PAGES = 400; const AUTHOR_BATCH = 150; // kind-0 lookups batched by author diff --git a/scripts/deploy-push.sh b/scripts/deploy-push.sh index 52b5bca..83ef107 100644 --- a/scripts/deploy-push.sh +++ b/scripts/deploy-push.sh @@ -48,7 +48,7 @@ scp -i "$CVM_SSH_KEY" -o BatchMode=yes \ echo "→ Creating .env with fresh VAPID keys if missing (private key stays on the host)" "${SSH[@]}" "cd $PUSH_DIR && if [ ! -f .env ]; then \ ~/.bun/bin/bun -e \"import wp from 'web-push'; const k=wp.generateVAPIDKeys(); \ -console.log(['PUSH_VAPID_SUBJECT=mailto:owner@skate-spots.org','PUSH_VAPID_PUBLIC_KEY='+k.publicKey,'PUSH_VAPID_PRIVATE_KEY='+k.privateKey,'PUSH_RELAYS=wss://goskatespots.nostr1.com','PUSH_MARKER=skatespots','PUSH_STORAGE_PATH=$PUSH_DIR/push-state.json','PUSH_ALLOWED_ORIGINS=https://skate-spots.org,http://localhost:8080','PORT=8788'].join('\\n'))\" > .env && chmod 600 .env && echo 'created .env'; \ +console.log(['PUSH_VAPID_SUBJECT=mailto:owner@skate-spots.org','PUSH_VAPID_PUBLIC_KEY='+k.publicKey,'PUSH_VAPID_PRIVATE_KEY='+k.privateKey,'PUSH_RELAYS=wss://relay.skate-spots.org','PUSH_MARKER=skatespots','PUSH_STORAGE_PATH=$PUSH_DIR/push-state.json','PUSH_ALLOWED_ORIGINS=https://skate-spots.org,http://localhost:8080','PORT=8788'].join('\\n'))\" > .env && chmod 600 .env && echo 'created .env'; \ else echo '.env already exists (leaving keys untouched)'; fi" echo "→ bun install" diff --git a/scripts/transfer-content.mjs b/scripts/transfer-content.mjs index 837e6f1..616c068 100644 --- a/scripts/transfer-content.mjs +++ b/scripts/transfer-content.mjs @@ -34,7 +34,7 @@ const SPOT = 30818; const SHOP = 30017; const DEFAULT_RELAYS = [ - 'wss://goskatespots.nostr1.com', + 'wss://relay.skate-spots.org', 'wss://relay.damus.io', 'wss://nos.lol', ]; diff --git a/src/App.tsx b/src/App.tsx index 83140ee..2fcc091 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -53,7 +53,8 @@ const defaultConfig: AppConfig = { relays: [ { url: 'wss://auth.nostr1.com', read: true, write: true }, { url: 'wss://relay.ditto.pub', read: true, write: true }, - { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, + { url: 'wss://relay.skate-spots.org', read: true, write: true }, + { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, // legacy fallback { url: 'wss://purplepag.es', read: true, write: false }, // Profile aggregator - read only ], updatedAt: 0, diff --git a/src/components/NostrProvider.tsx b/src/components/NostrProvider.tsx index 52da254..a5d1350 100644 --- a/src/components/NostrProvider.tsx +++ b/src/components/NostrProvider.tsx @@ -16,6 +16,7 @@ const DEFAULT_RELAYS = [ { url: 'wss://auth.nostr1.com', read: true, write: true }, { url: 'wss://relay.ditto.pub', read: true, write: true }, { url: SKATE_RELAY, read: true, write: true }, + { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, // legacy fallback { url: 'wss://purplepag.es', read: true, write: false }, // Profile aggregator ]; @@ -122,7 +123,7 @@ const NostrProvider: React.FC = (props) => { : DEFAULT_RELAYS.filter((r) => r.read).map((r) => r.url); // --- Community "catching" relay: top hierarchy --- - // goskatespots.nostr1.com is the app's curated, authoritative relay. + // relay.skate-spots.org is the app's curated, authoritative relay. // It is ALWAYS queried first (even if a user hasn't added it to their // read list), so curated skate content takes priority. Other relays act // as supplementary sources for redundancy and reach. diff --git a/src/components/NostrSync.tsx b/src/components/NostrSync.tsx index 5338503..4e21768 100644 --- a/src/components/NostrSync.tsx +++ b/src/components/NostrSync.tsx @@ -15,7 +15,8 @@ const CITRINE_URL = 'ws://localhost:4869'; const NEW_USER_RELAYS = [ { url: 'wss://auth.nostr1.com', read: true, write: true }, { url: 'wss://relay.ditto.pub', read: true, write: true }, - { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, + { url: 'wss://relay.skate-spots.org', read: true, write: true }, + { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, // legacy fallback { url: 'wss://purplepag.es', read: true, write: false }, ]; diff --git a/src/components/PlatformFeePayment.tsx b/src/components/PlatformFeePayment.tsx index e525b21..77b84fa 100644 --- a/src/components/PlatformFeePayment.tsx +++ b/src/components/PlatformFeePayment.tsx @@ -42,6 +42,7 @@ interface PlatformFeePaymentProps { // Relays the provider should publish the zap receipt to (so we can verify it). const ZAP_RECEIPT_RELAYS = [ + 'wss://relay.skate-spots.org', 'wss://goskatespots.nostr1.com', 'wss://relay.nostr.band', 'wss://relay.damus.io', diff --git a/src/components/SasquatchGuide.tsx b/src/components/SasquatchGuide.tsx index 2c0c8ea..979124b 100644 --- a/src/components/SasquatchGuide.tsx +++ b/src/components/SasquatchGuide.tsx @@ -235,7 +235,7 @@ Note: this returns a rich itinerary CARD — after calling it, add a short one-l - Cashu = built-in ecash wallet, private and works offline at the skatepark. Pay zaps straight from your Cashu balance. - Nutzaps (NIP-61) = ecash tips sent over Nostr; you can set up nutzap RECEIVING in the wallet (sending full nutzaps is rolling out — use Lightning zaps for now). - CLINK (noffer) = an optional reusable Nostr-native payment code a shop/crew/Lab pro can add so you can pay them without a Lightning address. Shows a "Pay via CLINK" button. -- goskatespots.nostr1.com = primary community relay +- relay.skate-spots.org = primary community relay - 100% free, no ads, no tracking, community-funded via Angor (see Supporters). 🛹`; // ─── Personal tools (Sasquatch Pro) — only offered when the user is logged in ─ @@ -1947,7 +1947,7 @@ Instructions: {t('sasquatchUi.liveDataFrom')}{' '} = { howToSellGear: "Ekipman Takası / Marketplace'e git ve bir ilan oluştur (NIP-99). Fotoğraf, sats cinsinden fiyat, durum ekle ve isteğe bağlı olarak haritada görünmesi için bir spota sabitle. Alıcılar sana doğrudan zap gönderir veya mesaj atar. 🛹💸", supporters: "SkateSpots, Angor (merkeziyetsiz Bitcoin kitle fonlaması) aracılığıyla topluluk tarafından fonlanır — reklam yok, yatırımcı yok, veri satışı yok. Projeyi desteklemek ve sokakları ücretsiz tutmak için Destekçiler sayfasına bak. ❤️⚡", trustScore: "Relatr tarafından desteklenen güven puanları (0-100). Nostr ağını analiz eder: takipçiler, etkinlik, etkileşim, zap'lar. Yüksek = daha güvenilir. Botları ve spam'ı filtrelemeye yardımcı olur. Puanını görmek için herhangi bir profile bak. 🛡️", - whatRelay: "SkateSpots şunları kullanır: 🛹 goskatespots.nostr1.com (birincil, kalite için allowlist'te) + relay.ditto.pub (küresel) + purplepag.es (profil toplayıcı). Verilerin yedeklilik için hepsinde kopyalanır.", + whatRelay: "SkateSpots şunları kullanır: 🛹 relay.skate-spots.org (birincil, kalite için allowlist'te) + relay.ditto.pub (küresel) + purplepag.es (profil toplayıcı). Verilerin yedeklilik için hepsinde kopyalanır.", tripPlanner: "Bana bir şehir söyle, sana gün gün bir kaykay gezisi kurayım 🗺️ — spotlar, etkinlikler, koçlar ve yerel crew'lar, havaya duyarlı (yağmurlu gün = kapalı spotlar için crew ipucu). 'Lizbon'da 3 gün planla' dene. Hepsini bir Koleksiyona kaydedebilirim ve seni gerçek sitelerdeki uçuş/konaklamalara yönlendirebilirim (KYC yok, aracı yok). 🛹", planATrip: "Sadece 'Barselona'da 3 gün planla' (veya herhangi bir şehir) de, her gün için spotları, seansları, koçları ve yerel crew'ları haritalayayım. Bir Koleksiyon olarak kaydet ve shred'e başla. 🗺️🛹", sasquatchPro: "**Sasquatch Pro** 🦧⚡, kişisel tarafımı açan bir Lightning destekçi pass'idir:\n\n• 🧠 **Kişisel asistan** — kendi Stash'ini, crew'larını, spotlarını ve ilanlarını okurum\n• ✨ **Eylem taslakları** — ekipman listele, kaykay durumunu paylaş, geziler planla (herhangi bir şey yayınlanmadan önce her zaman sen onaylarsın)\n• 💬 **Kişisel bağlam** — yanıtlarım yalnızca herkese açık sonuçları değil, kendi verini kullanır\n• ⚡ **Projeyi destekle** — SkateSpots'u ücretsiz ve reklamsız tutarsın\n\nÜcretsiz Sasquatch ücretsiz kalır: herkese açık spot/etkinlik/koç/crew araması, sözlük ve gezi planlama Pro olmadan da çalışır. Pro sadece *kişisel* şeyleri ekler. Yukarıdaki **⚡ Pro'ya Geç**'e dokun. Kademeler: 7 gün ~2k sats, 1 ay ~5k, 3 ay ~12k, 1 yıl ~36k.", diff --git a/src/i18n/uk/batch2.ts b/src/i18n/uk/batch2.ts index 4f58c87..b38f90f 100644 --- a/src/i18n/uk/batch2.ts +++ b/src/i18n/uk/batch2.ts @@ -1597,7 +1597,7 @@ const ukBatch2 = { howToSellGear: "Перейдіть до Gear Swap / Маркетплейсу й створіть оголошення (NIP-99). Додайте фото, ціну в sats, стан і за бажанням прив'яжіть до Spot, щоб воно з'явилося на карті. Покупці надсилають Zap або пишуть вам напряму. 🛹💸", supporters: "SkateSpots фінансується спільнотою через Angor (децентралізований краудфандинг на Bitcoin) — без реклами, без інвесторів, без продажу даних. Перегляньте сторінку Прихильників, щоб підтримати проєкт і зберегти вулиці вільними. ❤️⚡", trustScore: "Оцінки довіри (0–100) на базі Relatr. Аналізує вашу мережу Nostr: підписників, активність, залучення, Zaps. Вище = більше довіри. Допомагає фільтрувати ботів і спам. Перегляньте будь-який профіль, щоб побачити оцінку. 🛡️", - whatRelay: "SkateSpots використовує: 🛹 goskatespots.nostr1.com (основний, у списку дозволених для якості) + relay.ditto.pub (глобальний) + purplepag.es (агрегатор профілів). Ваші дані реплікуються на всіх них для надійності.", + whatRelay: "SkateSpots використовує: 🛹 relay.skate-spots.org (основний, у списку дозволених для якості) + relay.ditto.pub (глобальний) + purplepag.es (агрегатор профілів). Ваші дані реплікуються на всіх них для надійності.", tripPlanner: "Назвіть місто, і я збудую скейт-подорож день за днем 🗺️ — Spots, події, тренери та локальні Crews, з урахуванням погоди (дощовий день = порада від Crew щодо накритих Spots). Спробуйте «сплануй мені 3 дні в Лісабоні». Я можу зберегти все в Колекцію й передати вас до рейсів/проживання на справжніх сайтах (без KYC, без посередників). 🛹", planATrip: "Просто скажіть «сплануй мені 3 дні в Барселоні» (чи будь-якому місті), і я розпишу Spots, сесії, тренерів та локальні Crews на кожен день. Збережіть як Колекцію — і гайда катати. 🗺️🛹", sasquatchPro: "**Sasquatch Pro** 🦧⚡ — це Lightning-перепустка прихильника, що відкриває мою персональну сторону:\n\n• 🧠 **Персональний асистент** — я читаю ваш власний Stash, Crews, Spots та оголошення\n• ✨ **Чернетки дій** — виставити спорядження, опублікувати ваш скейт-статус, спланувати подорожі (ви завжди підтверджуєте, перш ніж щось публікується)\n• 💬 **Особистий контекст** — мої відповіді використовують ваші дані, а не лише публічні результати\n• ⚡ **Підтримка проєкту** — ви зберігаєте SkateSpots безкоштовним та без реклами\n\nБезкоштовний Sasquatch залишається безкоштовним: публічний пошук Spots/подій/тренерів/Crew, глосарій і планування подорожей працюють без Pro. Pro лише додає *персональні* речі. Натисніть **⚡ Go Pro** вгорі. Тарифи: 7 днів ~2k sats, 1 місяць ~5k, 3 місяці ~12k, 1 рік ~36k.", diff --git a/src/i18n/vi/batch2.ts b/src/i18n/vi/batch2.ts index 85674c3..e9f4ecb 100644 --- a/src/i18n/vi/batch2.ts +++ b/src/i18n/vi/batch2.ts @@ -1597,7 +1597,7 @@ const viBatch2 = { howToSellGear: 'Vào Trao đổi đồ nghề / Chợ và tạo tin đăng (NIP-99). Thêm ảnh, giá bằng sats, tình trạng, và tùy chọn ghim vào một spot để hiện trên bản đồ. Người mua zap hoặc nhắn trực tiếp cho bạn. 🛹💸', supporters: 'SkateSpots được cộng đồng tài trợ qua Angor (gây quỹ Bitcoin phi tập trung) — không quảng cáo, không nhà đầu tư, không bán dữ liệu. Xem trang Người ủng hộ để hậu thuẫn dự án và giữ đường phố tự do. ❤️⚡', trustScore: 'Điểm tin cậy (0-100) do Relatr cung cấp. Phân tích mạng lưới Nostr của bạn: người theo dõi, hoạt động, tương tác, zap. Càng cao = càng đáng tin. Giúp lọc bot và spam. Xem hồ sơ bất kỳ để thấy điểm. 🛡️', - whatRelay: 'SkateSpots dùng: 🛹 goskatespots.nostr1.com (chính, nằm trong danh sách cho phép để đảm bảo chất lượng) + relay.ditto.pub (toàn cầu) + purplepag.es (tổng hợp hồ sơ). Dữ liệu của bạn được nhân bản trên tất cả để dự phòng.', + whatRelay: 'SkateSpots dùng: 🛹 relay.skate-spots.org (chính, nằm trong danh sách cho phép để đảm bảo chất lượng) + relay.ditto.pub (toàn cầu) + purplepag.es (tổng hợp hồ sơ). Dữ liệu của bạn được nhân bản trên tất cả để dự phòng.', tripPlanner: 'Cho tôi một thành phố và tôi sẽ dựng chuyến trượt từng ngày 🗺️ — spot, sự kiện, coach và crew địa phương, có tính đến thời tiết (ngày mưa = gợi ý crew chỗ có mái). Thử \'lên kế hoạch 3 ngày ở Lisbon\'. Tôi có thể lưu tất cả vào một Bộ sưu tập, và chuyển bạn sang chuyến bay/chỗ ở trên các trang thật (không KYC, không trung gian). 🛹', planATrip: 'Chỉ cần nói \'lên kế hoạch 3 ngày ở Barcelona\' (hoặc bất kỳ thành phố nào) và tôi sẽ vạch ra spot, session, coach và crew địa phương cho mỗi ngày. Lưu thành một Bộ sưu tập và đi trượt. 🗺️🛹', sasquatchPro: '**Sasquatch Pro** 🦧⚡ là thẻ ủng hộ bằng Lightning mở khóa mặt cá nhân của tôi:\n\n• 🧠 **Trợ lý cá nhân** — tôi đọc Stash, crew, spot & tin đăng của chính bạn\n• ✨ **Bản nháp hành động** — đăng bán đồ, đăng trạng thái trượt, lên kế hoạch chuyến đi (bạn luôn xác nhận trước khi đăng)\n• 💬 **Ngữ cảnh cá nhân** — câu trả lời của tôi dùng dữ liệu của bạn, không chỉ kết quả công khai\n• ⚡ **Ủng hộ dự án** — bạn giữ SkateSpots miễn phí và không quảng cáo\n\nSasquatch miễn phí vẫn miễn phí: tìm spot/sự kiện/coach/crew công khai, bảng thuật ngữ & lập kế hoạch chuyến đi đều hoạt động không cần Pro. Pro chỉ thêm phần *cá nhân*. Nhấn **⚡ Lên Pro** ở trên cùng. Các hạng: 7 ngày ~2k sats, 1 tháng ~5k, 3 tháng ~12k, 1 năm ~36k.', diff --git a/src/i18n/zh-CN/batch2.ts b/src/i18n/zh-CN/batch2.ts index 3544d4f..4b8cb6b 100644 --- a/src/i18n/zh-CN/batch2.ts +++ b/src/i18n/zh-CN/batch2.ts @@ -1543,7 +1543,7 @@ const zhCNBatch2 = { howToSellGear: "前往装备交换 / Marketplace 创建商品(NIP-99)。添加照片、以 sats 定价、成色,并可选地把它固定到一个 Spot 上以显示在地图上。买家直接 Zap 或私信你。🛹💸", supporters: "SkateSpots 通过 Angor(去中心化 Bitcoin 众筹)由社区资助——没有广告、没有投资人、不售卖数据。查看支持者页面来支持这个项目,让街头保持自由。❤️⚡", trustScore: "信任分数(0-100)由 Relatr 提供。分析你的 Nostr 网络:粉丝、活跃度、互动、Zap。越高 = 越可信。有助于过滤机器人和垃圾信息。查看任意个人资料即可看到他们的分数。🛡️", - whatRelay: "SkateSpots 使用:🛹 goskatespots.nostr1.com(主 Relay,为保证质量而加入白名单)+ relay.ditto.pub(全球)+ purplepag.es(个人资料聚合器)。你的数据会复制到所有这些 Relay 以实现冗余。", + whatRelay: "SkateSpots 使用:🛹 relay.skate-spots.org(主 Relay,为保证质量而加入白名单)+ relay.ditto.pub(全球)+ purplepag.es(个人资料聚合器)。你的数据会复制到所有这些 Relay 以实现冗余。", tripPlanner: "告诉我一个城市,我会为你规划逐日的滑板之旅 🗺️——Spot、活动、教练和本地 Crew,并考虑天气(雨天 = 推荐有遮蔽 Spot 的 Crew)。试试“帮我规划里斯本 3 天”。我可以把它全部保存到合集,并把你引导到真实网站上的航班/住宿(无 KYC,无中间商)。🛹", planATrip: "只要说“帮我规划巴塞罗那 3 天”(或任何城市),我就会为每天规划 Spot、场次、教练和本地 Crew。保存为合集就去滑吧。🗺️🛹", sasquatchPro: "**Sasquatch Pro** 🦧⚡ 是一张 Lightning 支持者通行证,解锁我的个人功能:\n\n• 🧠 **个人助理**——我读取你自己的 Stash、Crew、Spot 和商品\n• ✨ **操作草稿**——列出装备、发布你的滑板状态、规划旅行(任何内容发布前你都会确认)\n• 💬 **个人背景**——我的回答会使用你自己的数据,而不只是公开结果\n• ⚡ **支持项目**——你让 SkateSpots 保持免费且无广告\n\n免费版 Sasquatch 始终免费:公开的 Spot/活动/教练/Crew 搜索、词汇表和旅行规划都无需 Pro。Pro 只是增加*个人*功能。点击顶部的 **⚡ 升级 Pro**。档位:7 天约 2k sats,1 个月约 5k,3 个月约 12k,1 年约 36k。", diff --git a/src/i18n/zh-TW/batch2.ts b/src/i18n/zh-TW/batch2.ts index bfaee65..8747d6f 100644 --- a/src/i18n/zh-TW/batch2.ts +++ b/src/i18n/zh-TW/batch2.ts @@ -1543,7 +1543,7 @@ const zhTWBatch2 = { howToSellGear: "前往裝備交換 / Marketplace 建立商品(NIP-99)。新增照片、以 sats 定價、成色,並可選地把它固定到一個 Spot 上以顯示在地圖上。買家直接 Zap 或私信你。🛹💸", supporters: "SkateSpots 透過 Angor(去中心化 Bitcoin 眾籌)由社群資助——沒有廣告、沒有投資人、不售賣資料。檢視支持者頁面來支援這個專案,讓街頭保持自由。❤️⚡", trustScore: "信任分數(0-100)由 Relatr 提供。分析你的 Nostr 網路:粉絲、活躍度、互動、Zap。越高 = 越可信。有助於過濾機器人和垃圾資訊。檢視任意個人資料即可看到他們的分數。🛡️", - whatRelay: "SkateSpots 使用:🛹 goskatespots.nostr1.com(主 Relay,為保證品質而加入白名單)+ relay.ditto.pub(全球)+ purplepag.es(個人資料聚合器)。你的資料會複製到所有這些 Relay 以實現冗餘。", + whatRelay: "SkateSpots 使用:🛹 relay.skate-spots.org(主 Relay,為保證品質而加入白名單)+ relay.ditto.pub(全球)+ purplepag.es(個人資料聚合器)。你的資料會複製到所有這些 Relay 以實現冗餘。", tripPlanner: "告訴我一個城市,我會為你規劃逐日的滑板之旅 🗺️——Spot、活動、教練和本地 Crew,並考慮天氣(雨天 = 推薦有遮蔽 Spot 的 Crew)。試試“幫我規劃里斯本 3 天”。我可以把它全部儲存到合集,並把你引導到真實網站上的航班/住宿(無 KYC,無中間商)。🛹", planATrip: "只要說“幫我規劃巴塞羅那 3 天”(或任何城市),我就會為每天規劃 Spot、場次、教練和本地 Crew。儲存為合集就去滑吧。🗺️🛹", sasquatchPro: "**Sasquatch Pro** 🦧⚡ 是一張 Lightning 支持者通行證,解鎖我的個人功能:\n\n• 🧠 **個人助理**——我讀取你自己的 Stash、Crew、Spot 和商品\n• ✨ **操作草稿**——列出裝備、發布你的滑板狀態、規劃旅行(任何內容發布前你都會確認)\n• 💬 **個人背景**——我的回答會使用你自己的資料,而不只是公開結果\n• ⚡ **支援專案**——你讓 SkateSpots 保持免費且無廣告\n\n免費版 Sasquatch 始終免費:公開的 Spot/活動/教練/Crew 搜尋、詞彙表和旅行規劃都無需 Pro。Pro 只是增加*個人*功能。點選頂部的 **⚡ 升級 Pro**。檔位:7 天約 2k sats,1 個月約 5k,3 個月約 12k,1 年約 36k。", diff --git a/src/lib/relayAdmin.ts b/src/lib/relayAdmin.ts index 9f925ce..700ff5a 100644 --- a/src/lib/relayAdmin.ts +++ b/src/lib/relayAdmin.ts @@ -1,11 +1,13 @@ /** * NIP-86 Relay Management API client (with NIP-98 HTTP auth). * - * The SkateSpots community relay (goskatespots.nostr1.com) runs on relay.tools, - * which implements the NIP-86 management API. As the relay OWNER (or an assigned - * moderator), an admin can genuinely remove an event from the relay's database — - * not merely publish a best-effort kind:5 deletion request that other relays may - * ignore. `banevent` is the method that does this. + * The SkateSpots community relay (relay.skate-spots.org) is our own Newlay + * instance (self-hosted since 2026-10-12); Newlay advertises NIP-86. As the + * relay OWNER (or an assigned moderator), an admin can genuinely remove an + * event from the relay's database — not merely publish a best-effort kind:5 + * deletion request that other relays may ignore. `banevent` is the method that + * does this. (NIP-86 admin rights must be configured relay-side; until then a + * call 401s and the admin UI falls back to NIP-32 label hiding.) * * Auth is a NIP-98 kind:27235 event signed by the caller (via their Amber / * NIP-07 / nsec signer). The relay checks the signer's pubkey against its owner diff --git a/src/lib/types.ts b/src/lib/types.ts index dd858bb..b15055c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -8,7 +8,7 @@ import { SPOT_FEATURE_KEYS } from '@/lib/labelKeys'; // Community relay for all skate-specific content (spots, events, coaches, shops, etc.) // This ensures all users see the same content regardless of their personal relay settings -export const SKATE_RELAY = 'wss://goskatespots.nostr1.com'; +export const SKATE_RELAY = 'wss://relay.skate-spots.org'; // ─── Validation helpers ─────────────────────────────────────────────────────── diff --git a/src/pages/NewsletterPage.tsx b/src/pages/NewsletterPage.tsx index 81ce598..78cf852 100644 --- a/src/pages/NewsletterPage.tsx +++ b/src/pages/NewsletterPage.tsx @@ -288,7 +288,7 @@ function LiveStatsBlock() { ))}

- {t('walletUi.newsletter.live')} · goskatespots.nostr1.com + {t('walletUi.newsletter.live')} · relay.skate-spots.org

); diff --git a/src/test/TestApp.tsx b/src/test/TestApp.tsx index 62bf01c..8ccfb13 100644 --- a/src/test/TestApp.tsx +++ b/src/test/TestApp.tsx @@ -29,7 +29,8 @@ export function TestApp({ children }: TestAppProps) { relays: [ { url: 'wss://auth.nostr1.com', read: true, write: true }, { url: 'wss://relay.ditto.pub', read: true, write: true }, - { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, + { url: 'wss://relay.skate-spots.org', read: true, write: true }, + { url: 'wss://goskatespots.nostr1.com', read: true, write: true }, // legacy fallback { url: 'wss://purplepag.es', read: true, write: false }, ], updatedAt: 0,