feat: relay slides (outbox model, categories, access asymmetry)
Explain the outbox/gossip model and Amethyst's relay categories, plus a read/write asymmetry table showing why outbox / inbox / DM need different relay sets and what breaks if a list is empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
07f98a04dc
commit
ba9d4912ac
3 changed files with 190 additions and 0 deletions
45
src/components/slides/OutboxSlide.vue
Normal file
45
src/components/slides/OutboxSlide.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PenLine, ArrowRight, Server, Users } from '@lucide/vue'
|
||||||
|
import SlideShell from './SlideShell.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SlideShell kicker="The key idea" title="The outbox model (a.k.a. gossip)">
|
||||||
|
<!-- Flow: you write → your outbox relays → followers read -->
|
||||||
|
<div class="flex flex-col items-stretch gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div class="flex flex-1 flex-col items-center gap-2 rounded-xl border border-zinc-700 bg-zinc-900/60 p-5 text-center">
|
||||||
|
<PenLine class="h-8 w-8 text-violet-400" />
|
||||||
|
<p class="font-semibold text-white">You post</p>
|
||||||
|
<p class="text-sm text-zinc-400">A signed note</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ArrowRight class="mx-auto h-6 w-6 rotate-90 text-zinc-500 sm:rotate-0" />
|
||||||
|
|
||||||
|
<div class="flex flex-1 flex-col items-center gap-2 rounded-xl border border-violet-600/50 bg-violet-950/20 p-5 text-center">
|
||||||
|
<Server class="h-8 w-8 text-violet-300" />
|
||||||
|
<p class="font-semibold text-white">Your <span class="text-violet-300">write / outbox</span> relays</p>
|
||||||
|
<p class="text-sm text-zinc-400">Where your content lives</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ArrowRight class="mx-auto h-6 w-6 rotate-90 text-zinc-500 sm:rotate-0" />
|
||||||
|
|
||||||
|
<div class="flex flex-1 flex-col items-center gap-2 rounded-xl border border-zinc-700 bg-zinc-900/60 p-5 text-center">
|
||||||
|
<Users class="h-8 w-8 text-violet-400" />
|
||||||
|
<p class="font-semibold text-white">Followers read</p>
|
||||||
|
<p class="text-sm text-zinc-400">Their app looks there</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 space-y-4">
|
||||||
|
<p>
|
||||||
|
Your app <span class="font-semibold text-white">announces</span> which relays you write to.
|
||||||
|
Other people’s apps read that list and go fetch your notes <span class="text-white">from the right place</span> —
|
||||||
|
no central server required.
|
||||||
|
</p>
|
||||||
|
<p class="rounded-lg border-l-2 border-violet-500 bg-zinc-900/50 py-3 pl-4 pr-3 text-zinc-300">
|
||||||
|
“If you don’t know where your posts are, you might as well just stay on centralized Twitter.”
|
||||||
|
<span class="text-zinc-500">— Vitor Pamplona, Amethyst</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</SlideShell>
|
||||||
|
</template>
|
||||||
70
src/components/slides/RelayAccessSlide.vue
Normal file
70
src/components/slides/RelayAccessSlide.vue
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Home, Bell, Mail } from '@lucide/vue'
|
||||||
|
import SlideShell from './SlideShell.vue'
|
||||||
|
|
||||||
|
const rows = [
|
||||||
|
{
|
||||||
|
icon: Home,
|
||||||
|
role: 'Outbox',
|
||||||
|
writes: 'You',
|
||||||
|
reads: 'Everyone',
|
||||||
|
empty: 'Followers never get your posts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Bell,
|
||||||
|
role: 'Inbox',
|
||||||
|
writes: 'Everyone',
|
||||||
|
reads: 'You',
|
||||||
|
empty: 'You miss replies & mentions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Mail,
|
||||||
|
role: 'DM inbox',
|
||||||
|
writes: 'Anyone (encrypted)',
|
||||||
|
reads: 'Only you',
|
||||||
|
empty: 'You miss DMs',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SlideShell kicker="Amethyst — why the sets differ" title="One relay can’t do every job">
|
||||||
|
<div class="overflow-hidden rounded-xl border border-zinc-700">
|
||||||
|
<table class="w-full text-left text-base">
|
||||||
|
<thead class="bg-zinc-800/60 text-sm uppercase tracking-wider text-zinc-400">
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-3 font-medium">Role</th>
|
||||||
|
<th class="px-4 py-3 font-medium">Who writes</th>
|
||||||
|
<th class="px-4 py-3 font-medium">Who reads</th>
|
||||||
|
<th class="px-4 py-3 font-medium text-red-300">If you leave it empty</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-zinc-800">
|
||||||
|
<tr v-for="row in rows" :key="row.role">
|
||||||
|
<td class="px-4 py-3 font-semibold text-white">
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<component :is="row.icon" class="h-5 w-5 text-violet-400" />
|
||||||
|
{{ row.role }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-zinc-300">{{ row.writes }}</td>
|
||||||
|
<td class="px-4 py-3 text-zinc-300">{{ row.reads }}</td>
|
||||||
|
<td class="px-4 py-3 text-red-300/90">{{ row.empty }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mt-6">
|
||||||
|
The access rules are <span class="font-semibold text-white">opposite</span>, so no single relay
|
||||||
|
fits every list — that’s the whole reason for separate sets.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-4 rounded-lg border-l-2 border-violet-500 bg-zinc-900/50 py-3 pl-4 pr-3 text-zinc-300">
|
||||||
|
<span class="font-semibold text-white">Reuse vs. keep separate:</span>
|
||||||
|
an <em>open</em> relay (e.g. <code class="text-zinc-200">nos.lol</code>) is fine in several lists at once.
|
||||||
|
A <em>paid / restricted</em> relay (e.g. <code class="text-zinc-200">nostr.wine</code>) must stay in its lane —
|
||||||
|
great for DMs, wrong for your public inbox.
|
||||||
|
</div>
|
||||||
|
</SlideShell>
|
||||||
|
</template>
|
||||||
75
src/components/slides/RelayCategoriesSlide.vue
Normal file
75
src/components/slides/RelayCategoriesSlide.vue
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Home, Bell, Lock, Mail, Search, HardDrive } from '@lucide/vue'
|
||||||
|
import SlideShell from './SlideShell.vue'
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{
|
||||||
|
icon: Home,
|
||||||
|
name: 'Public Home / Outbox',
|
||||||
|
count: '~3',
|
||||||
|
what: 'Where your posts, likes & replies live. Followers read from here.',
|
||||||
|
tip: 'One big public relay + one personal + one fast/country relay.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Bell,
|
||||||
|
name: 'Public Inbox',
|
||||||
|
count: '~3',
|
||||||
|
what: 'Where replies, mentions & zaps arrive — your notifications.',
|
||||||
|
tip: 'Keep separate from outbox. Avoid nostr.wine variants here.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Mail,
|
||||||
|
name: 'DM Inbox',
|
||||||
|
count: '~3',
|
||||||
|
what: 'Encrypted direct messages. Write-open, read-restricted to you.',
|
||||||
|
tip: 'inbox.nostr.wine or auth.nostr1.com + a personal backup.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Lock,
|
||||||
|
name: 'Private Home',
|
||||||
|
count: '1–2',
|
||||||
|
what: 'Drafts, bookmarks, lists & app settings — nobody else reads these.',
|
||||||
|
tip: 'A local relay (Citrine) or an authed personal relay.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Search,
|
||||||
|
name: 'Search',
|
||||||
|
count: '2–3',
|
||||||
|
what: 'Powers tagging & search. Must support NIP-50.',
|
||||||
|
tip: 'relay.nostr.band, nostr.wine, relay.noswhere.com.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: HardDrive,
|
||||||
|
name: 'Local',
|
||||||
|
count: '1',
|
||||||
|
what: 'On-device cache so the app is fast & works offline.',
|
||||||
|
tip: 'Citrine on Android → ws://localhost:4869.',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SlideShell kicker="Amethyst — the confusing part" title="Relays come in categories">
|
||||||
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
<div
|
||||||
|
v-for="cat in categories"
|
||||||
|
:key="cat.name"
|
||||||
|
class="rounded-xl border border-zinc-700 bg-zinc-900/60 p-5"
|
||||||
|
>
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<component :is="cat.icon" class="h-6 w-6 text-violet-400" />
|
||||||
|
<span class="rounded-full bg-violet-600/20 px-2 py-0.5 text-xs font-semibold text-violet-300">
|
||||||
|
{{ cat.count }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-lg font-semibold text-white">{{ cat.name }}</p>
|
||||||
|
<p class="mt-1 text-sm leading-snug text-zinc-400">{{ cat.what }}</p>
|
||||||
|
<p class="mt-3 text-xs leading-snug text-violet-300/90">→ {{ cat.tip }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-6 text-sm text-zinc-500">
|
||||||
|
Don’t overthink it: set <span class="text-zinc-300">outbox</span>, <span class="text-zinc-300">inbox</span> and
|
||||||
|
<span class="text-zinc-300">DM</span> with ~3 solid relays each. Amethyst suggests the rest from who you follow.
|
||||||
|
</p>
|
||||||
|
</SlideShell>
|
||||||
|
</template>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue