import { A, useNavigate } from "@solidjs/router" import { createSignal, Show } 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 PaymentDialog from "@/components/PaymentDialog" import PaymentSetup from "@/components/PaymentSetup" import Login from "@/views/Login" import { createRelayForActiveTenant, resolvePostPaidFlow } from "@/lib/hooks" import type { Invoice } from "@/lib/api" import { account, refetchBilling, setToastMessage } from "@/lib/state" import FlotillaLogo from "@/assets/flotilla-logo.svg" import NostordLogo from "@/assets/nostord-logo.svg" export default function Home() { const navigate = useNavigate() const [showRelayModal, setShowRelayModal] = createSignal(false) const [showLoginModal, setShowLoginModal] = createSignal(false) const [draftRelay, setDraftRelay] = createSignal() const [initialPlanId, setInitialPlanId] = createSignal("free") const [pendingInvoice, setPendingInvoice] = createSignal() const [paymentSetupOpen, setPaymentSetupOpen] = createSignal(false) let createdRelayId = "" function openRelayModal(planId: RelayFormValues["plan_id"] = "free") { setInitialPlanId(planId) setShowRelayModal(true) } async function onRelayFormSubmit(values: RelayFormValues) { // Not signed in yet: stash the draft and send them through login. The relay // (and any payment prompt) is created in onAuthenticated once the session and // its tenant exist, so signing up and creating a paid relay in one go still // surfaces the invoice. if (!account()) { setDraftRelay(values) setShowRelayModal(false) setShowLoginModal(true) return } const relay = await createRelayForActiveTenant(values) createdRelayId = relay.id setShowRelayModal(false) // Paid plans materialize an open invoice on create. A just-signed-up tenant // has no payment method yet, so open the payment dialog here instead of // dropping them on the relay page with no prompt (the shared dashboard banner // only catches up once they navigate and its billing reads refetch). if (values.plan_id !== "free") { void refetchBilling() const decision = await resolvePostPaidFlow() if (decision.kind === "pay_invoice") { setPendingInvoice(decision.invoice) return } if (decision.kind === "setup") { setPaymentSetupOpen(true) return } } navigate(`/relays/${relay.id}`) } async function onAuthenticated() { setShowLoginModal(false) const relay = draftRelay() setDraftRelay(undefined) if (!relay) { navigate("/relays") return } try { await onRelayFormSubmit(relay) } catch (e) { setToastMessage(e instanceof Error ? e.message : "Failed to create relay") } } function handleInvoiceClose() { setPendingInvoice(undefined) setPaymentSetupOpen(true) } function handleSetupClose() { setPaymentSetupOpen(false) void refetchBilling() navigate(`/relays/${createdRelayId}`) } return (
{/* ── Nav ── */} {/* ── Hero ── */}
{/* Background decorations */}
Nostr-native relay hosting

Your community,
your server.

Spin up a private, managed Nostr relay for your community in minutes, with full control over membership, access, and policies.

{ if (!account()) { e.preventDefault() setShowLoginModal(true) } }} class="inline-flex items-center gap-2 py-3 px-8 border border-gray-200 text-gray-700 font-semibold rounded-xl hover:bg-gray-50 transition-all" > {account() ? "Go to dashboard" : "Sign in"}
{/* ── 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: "Flexible payments", body: "Pay with Bitcoin/Lightning or with a card.", }, ].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

Upgrade or cancel any time.

{/* ── CTA ── */}

Ready to launch your relay?

Join communities already running on Caravel. Set up in minutes.

{/* ── Footer ── */} 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.

setShowLoginModal(false)} wrapperClass="fixed inset-0 z-50 flex items-center justify-center bg-black/50" panelClass="w-full max-w-3xl mx-4 rounded-2xl" > setShowLoginModal(false)} onAuthenticated={onAuthenticated} /> {(inv) => ( )}
) }