diff --git a/AGENTS.md b/AGENTS.md index 0870ca6..e989cb2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -616,9 +616,17 @@ session with **no other memory of this project**. So: Cordn's phone app (an nprofile, not a URL) was un-enterable — `CrewForm` now uses the shared `parseCrewCoordinatorInput` / `crewCoordinatorInputValue` (`lib/coordinator.ts`) and has a **"Use my coordinator"** button that prefills - from Settings → Coordinator. **Still future:** real MLS - (`ts-mls`) instead of group-key crypto. **Lesson: grep `src` for the feature - before building it — this was nearly duplicated.** + from Settings → Coordinator. ⚠️ **Transport reality (do not overclaim):** a + crew's **`wss://` coordinator URL is used AS A RELAY** (`resolveCordnEndpoint` + → `relayUrl`); an `https://` one falls back to the **public** + `wss://relay.contextvm.org`. The app publishes sealed kind-4497 events and + queries them back — it does **not** call Cordn's coordinator RPC + (`FetchGroupMessages`/`SubscribeGroupMessages`), so a real Cordn coordinator is + not yet engaged, and an `https://` coordinator puts ciphertext + metadata + (crew coord, timing) on a public relay. `CrewCoordinatorCard` + `CrewForm` now + say this honestly (2026-09-29). **Still future:** real MLS + (`ts-mls`) instead of group-key crypto, plus Cordn RPC interop. **Lesson: grep + `src` for the feature before building it — this was nearly duplicated.** - **Empty-map nudge (2026-09-28):** `MapPage` distinguishes the two empties — the existing "No spots match" card is for *filters* (`filteredSpots.length === 0 && allLocations.length > 0`) and a new "No spots mapped yet — be the first" card diff --git a/src/components/CrewForm.tsx b/src/components/CrewForm.tsx index 1fa000e..00a6031 100644 --- a/src/components/CrewForm.tsx +++ b/src/components/CrewForm.tsx @@ -1086,7 +1086,7 @@ export function CrewForm({ existing }: CrewFormProps) { const coordinatorBlock = (
You don't need this. Your crew already has private, @@ -1101,7 +1101,10 @@ export function CrewForm({ existing }: CrewFormProps) { Cordn coordinator {' '} — even on an old phone. Paste its nprofile, a cordn.net link, or an - endpoint URL. Leave blank to use the built-in chat. + endpoint URL. A wss:// endpoint is used as the crew's own chat relay + (point it at a private one to keep even the metadata off public relays); an{' '} + https:// coordinator is addressed by its pubkey over the shared + ContextVM relay. Leave blank to use the built-in chat.
} + {endpoint && ( ++ Delivered over{' '} + {urlDomain(endpoint.relayUrl) || endpoint.relayUrl} + {overPublicRelay ? ' — the shared ContextVM relay.' : " — your crew's own relay."} +
+ )} +
- The Chat tab runs through this - coordinator: messages are end-to-end encrypted (NIP-44, sealed to each member) and routed - over ContextVM, so the coordinator only ever handles sealed traffic and nothing - crew-specific hits a public relay. + The Chat tab routes through this + coordinator: messages are end-to-end encrypted (NIP-44, sealed to each member), so it only + ever handles sealed traffic.{' '} + {overPublicRelay + ? 'Heads up: it is addressed over the shared ContextVM relay, so the ciphertext and its metadata (which crew, and when) are publicly visible — just not readable. Point the crew at a private wss:// relay to keep even that private.' + : "Nothing crew-specific leaves your crew's own relay."}