import { A, useNavigate } from "@solidjs/router" import { createSignal } from "solid-js" import CheckIcon from "@/components/CheckIcon" import ExternalLinkIcon from "@/components/ExternalLinkIcon" import PricingTable from "@/components/PricingTable" import RelayForm, { type RelayFormValues } from "@/components/RelayForm" import Modal from "@/components/Modal" import Login from "@/views/Login" import { createRelayForActiveTenant } from "@/lib/hooks" import { account } from "@/lib/state" import { slugify } from "@/lib/slugify" export default function Home() { const navigate = useNavigate() const [showRelayModal, setShowRelayModal] = createSignal(false) const [showLoginModal, setShowLoginModal] = createSignal(false) const [name, setName] = createSignal("") const [subdomain, setSubdomain] = createSignal("") const [icon, setIcon] = createSignal("") const [description, setDescription] = createSignal("") const [pendingRelay, setPendingRelay] = createSignal(false) const [error, setError] = createSignal("") function resetForm() { setName("") setSubdomain("") setIcon("") setDescription("") setError("") setPendingRelay(false) } function openRelayFlow() { setError("") setShowRelayModal(true) } async function createRelayAndRedirect() { setError("") setPendingRelay(true) try { const relay = await createRelayForActiveTenant({ subdomain: slugify(subdomain()), plan: "free", info_name: name().trim(), info_icon: icon().trim(), info_description: description().trim(), policy_public_join: 0, policy_strip_signatures: 0, groups_enabled: 1, management_enabled: 1, blossom_enabled: 0, livekit_enabled: 0, push_enabled: 1, }) setShowLoginModal(false) setShowRelayModal(false) resetForm() navigate(`/relays/${relay.id}`) } catch (e) { setError(e instanceof Error ? e.message : "Failed to create relay") setShowLoginModal(false) setShowRelayModal(true) } finally { setPendingRelay(false) } } async function handleRelaySubmit(values: RelayFormValues, e: Event) { e.preventDefault() setError("") setName(values.info_name) setSubdomain(values.subdomain) setIcon(values.info_icon) setDescription(values.info_description) if (account()) { await createRelayAndRedirect() return } setShowRelayModal(false) setShowLoginModal(true) } return (
{/* ── Nav ── */} {/* ── Hero ── */}
{/* Background decorations */}
Nostr-native relay hosting

Your community,
your relay.

Spin up a private, managed Nostr relay for your community in minutes. Full control over membership, access, and policies — no DevOps required.

{/* ── Features ── */}

Everything you need

Caravel takes care of the infrastructure so you can focus on building your community.

{[ { icon: ( ), title: "Managed hosting", body: "We handle uptime, backups, and updates. Your relay stays online so your community never misses a beat.", }, { icon: ( ), title: "Membership control", body: "Approve members with Nostr pubkeys. Keep your space invite-only or open — you decide.", }, { icon: ( ), title: "Access policies", body: "Fine-grained write and read permissions. Moderate content without touching any server config.", }, { icon: ( ), title: "Blossom storage", body: "Attach media files to your relay with integrated Blossom server support on paid plans.", }, { icon: ( ), title: "LiveKit video", body: "Built-in video room support via LiveKit. Host voice and video calls directly within your community.", }, { icon: ( ), title: "Pay with sats", body: "Lightning-native billing. No credit cards, no bank accounts — just sats, straight from your wallet.", }, ].map(({ icon, title, body }) => (
{icon}

{title}

{body}

))}
{/* ── Connect ── */}

Connect with your community

Once your relay is live, these Nostr-native platforms let your members connect, chat, and collaborate — all powered by your relay.

{/* ── Pricing ── */}

Simple pricing

Pay in sats. Upgrade or cancel any time.

{/* ── CTA ── */}

Ready to launch your relay?

Join communities already running on Caravel. Set up in minutes, pay in sats.

{/* ── Footer ── */} { if (!pendingRelay()) setShowRelayModal(false) }} wrapperClass="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" panelClass="w-full max-w-2xl rounded-2xl bg-white p-6 sm:p-8 shadow-2xl" >

Create your relay

Start with a free relay. You can upgrade later.

{ if (!pendingRelay()) setShowLoginModal(false) }} wrapperClass="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" panelClass="w-full max-w-4xl rounded-2xl" > { if (!pendingRelay()) setShowLoginModal(false) }} onAuthenticated={async () => { if (name().trim() && subdomain().trim()) { await createRelayAndRedirect() return } setShowLoginModal(false) navigate("/relays") }} />
) }