mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
1168 lines
77 KiB
HTML
1168 lines
77 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Uncloud Hub | The observability stack you never have to build</title>
|
|
<meta name="description"
|
|
content="Uncloud Hub connects to your Uncloud clusters and gives you a dashboard, metrics, logs, and alerts out of the box. See what's happening in production to catch issues early and fix faster.">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<link rel="canonical" href="https://uncloud.run/hub">
|
|
<link href="./style.css" rel="stylesheet">
|
|
<link rel="apple-touch-icon" href="./images/apple-touch-icon.png" sizes="180x180"/>
|
|
<link rel="icon" type="image/svg+xml" href="./images/logo.svg"/>
|
|
<link rel="alternate icon" type="image/png" href="./images/favicon.png" sizes="96x96"/>
|
|
|
|
<!-- OpenGraph tags for social media -->
|
|
<meta property="og:title" content="Uncloud Hub: the observability stack for your Uncloud clusters"/>
|
|
<meta property="og:description"
|
|
content="Uncloud Hub connects to your Uncloud clusters and gives you a dashboard, metrics, logs, and alerts out of the box. See what's happening in production to catch issues early and fix faster."/>
|
|
<meta property="og:image" content="https://uncloud.run/images/og-hub.png"/>
|
|
<meta property="og:image:alt" content="Uncloud Hub: the observability stack you never have to build"/>
|
|
<meta property="og:url" content="https://uncloud.run/hub"/>
|
|
<meta property="og:type" content="website"/>
|
|
<meta property="og:site_name" content="Uncloud"/>
|
|
<!-- Twitter card tags -->
|
|
<meta property="twitter:domain" content="uncloud.run">
|
|
<meta property="twitter:url" content="https://uncloud.run/hub">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="Uncloud Hub: the observability stack for your Uncloud clusters">
|
|
<meta name="twitter:description"
|
|
content="Uncloud Hub connects to your Uncloud clusters and gives you a dashboard, metrics, logs, and alerts out of the box. See what's happening in production to catch issues early and fix faster. ">
|
|
<meta name="twitter:image" content="https://uncloud.run/images/og-hub.png">
|
|
<meta name="twitter:image:alt" content="Uncloud Hub: the observability stack you never have to build">
|
|
|
|
<script src="./js/posthog.js" defer></script>
|
|
</head>
|
|
|
|
<body class="font-inter antialiased bg-white text-zinc-900 tracking-tight">
|
|
|
|
<!-- Page wrapper -->
|
|
<div class="flex flex-col min-h-screen overflow-hidden supports-[overflow:clip]:overflow-clip">
|
|
|
|
<!-- Site header -->
|
|
<header class="absolute top-2 md:top-6 w-full z-30">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="flex items-center justify-between h-14">
|
|
<!-- Site branding -->
|
|
<div class="shrink-0 mr-4">
|
|
<a class="flex items-center" href="/">
|
|
<img class="w-8 h-8 rounded-lg shadow-sm shadow-zinc-950/20" src="./images/logo.svg" alt="Logo"
|
|
width="32" height="32">
|
|
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Navigation links -->
|
|
<nav class="flex grow">
|
|
<!-- Left side navigation -->
|
|
<ul class="flex items-center">
|
|
<li>
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition inline-flex items-center"
|
|
href="/hub">
|
|
Hub
|
|
<span class="inline-block ml-1 -translate-y-1.5 text-[10px] font-semibold uppercase tracking-wide text-violet-700 bg-violet-100 px-1.5 py-0.5 rounded">
|
|
New
|
|
</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
|
|
href="/docs">Docs</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
|
|
href="/blog">Blog</a>
|
|
</li>
|
|
<!-- Hidden on small screens to keep the nav from overflowing at 390px. -->
|
|
<li class="hidden md:block">
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
|
|
href="https://psviderski.substack.com/" target="_blank">Newsletter</a>
|
|
</li>
|
|
</ul>
|
|
<!-- Right side navigation -->
|
|
<ul class="flex grow justify-end items-center shrink-0">
|
|
<li>
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 flex items-center transition"
|
|
href="https://discord.gg/eR35KQJhPu">
|
|
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24">
|
|
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
|
|
</svg>
|
|
<span class="ml-1 hidden sm:inline">Discord</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-1 sm:px-5 py-2 flex items-center transition"
|
|
href="https://github.com/psviderski/uncloud">
|
|
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24">
|
|
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
|
</svg>
|
|
<span class="ml-1 hidden sm:inline">GitHub</span>
|
|
<!-- Populated only when the star count is successfully fetched from the GitHub API. -->
|
|
<span class="js-github-stars text-zinc-400 ml-1 hidden sm:inline tabular-nums"></span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Page content -->
|
|
<main class="grow">
|
|
|
|
<!-- Hero -->
|
|
<section
|
|
class="relative before:absolute before:inset-0 before:h-80 before:pointer-events-none before:bg-gradient-to-b before:from-zinc-100 before:-z-10">
|
|
<div class="pt-32 pb-12 md:pt-40 md:pb-20">
|
|
<div class="px-4 sm:px-6">
|
|
<div class="max-w-3xl mx-auto">
|
|
<div class="text-center pb-12 md:pb-16">
|
|
<p class="mb-5 font-mono text-xs font-medium uppercase tracking-[0.25em] text-violet-600">
|
|
Coming soon
|
|
</p>
|
|
<h1 class="font-inter-tight text-4xl md:text-6xl font-extrabold bg-clip-text text-transparent bg-gradient-to-r from-zinc-500 via-zinc-900 to-zinc-900 pb-4">
|
|
The observability stack<br class="max-md:hidden">
|
|
<span class="text-violet-600">you never have to build</span>
|
|
</h1>
|
|
<p class="text-lg text-zinc-500 mb-8">
|
|
<b>Uncloud Hub</b> connects to your Uncloud clusters and gives you a dashboard,
|
|
metrics, logs, and alerts out of the box. See what's happening in production to catch
|
|
issues early and fix faster.
|
|
</p>
|
|
<div class="max-w-xs mx-auto sm:max-w-none sm:inline-flex sm:justify-center space-y-4 sm:space-y-0 sm:space-x-4">
|
|
<div>
|
|
<a class="btn text-zinc-100 bg-zinc-900 hover:bg-zinc-800 w-full shadow"
|
|
href="#early-access">
|
|
Get early access
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<a class="btn text-zinc-600 bg-white hover:text-zinc-900 w-full shadow flex items-center justify-center"
|
|
href="#how-it-works">
|
|
How it works
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hero image -->
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6 pb-12 md:pb-20">
|
|
<div class="flex justify-center relative before:absolute before:-top-12 before:w-96 before:h-96 before:bg-violet-700 before:opacity-[.15] before:rounded-full before:blur-3xl before:-z-10">
|
|
<div class="hidden md:block absolute -top-9 left-16 lg:left-32 z-10">
|
|
<span class="hw-note inline-block -rotate-3 text-zinc-600">your whole cluster on one screen</span>
|
|
<svg class="inline-block w-10 h-10 ml-1 translate-y-4 text-zinc-400"
|
|
width="40" height="40"
|
|
viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path d="M4 6c14 2 26 10 30 28M34 34l-8-3.5M34 34l1.5-8.5" stroke="currentColor"
|
|
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
</svg>
|
|
</div>
|
|
<img class="rounded-lg shadow-2xl" src="./images/hub-hero.webp" width="2560" height="1520"
|
|
alt="Uncloud Hub services dashboard">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Trust / stats -->
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<p class="text-center text-base text-zinc-500 mb-8">
|
|
Built on <a class="underline hover:text-zinc-900" href="/">Uncloud</a>,
|
|
the open-source multi-node Docker orchestrator that runs apps across your servers.
|
|
</p>
|
|
<div class="max-w-sm mx-auto grid grid-cols-2 gap-8 sm:gap-12 md:grid-cols-4 md:-mx-5 md:gap-0 items-start md:max-w-none">
|
|
|
|
<!-- GitHub stars -->
|
|
<a href="https://github.com/psviderski/uncloud" target="_blank" rel="noopener"
|
|
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
|
|
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span
|
|
x-data="counter(githubStarsK)" x-text="counterValue">5.3</span>K+</h4>
|
|
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">GitHub
|
|
stars</p>
|
|
</a>
|
|
|
|
<!-- Downloads -->
|
|
<a href="https://github.com/psviderski/uncloud/releases" target="_blank" rel="noopener"
|
|
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
|
|
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span
|
|
x-data="counter(downloadsK)" x-text="counterValue">21</span>K+</h4>
|
|
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">Downloads</p>
|
|
</a>
|
|
|
|
<!-- Hacker News front-page -->
|
|
<a href="https://hn.algolia.com/?q=uncloud" target="_blank" rel="noopener"
|
|
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
|
|
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span
|
|
x-data="counter(2)" x-text="counterValue">2</span>x</h4>
|
|
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">Hacker News
|
|
front page</p>
|
|
</a>
|
|
|
|
<!-- Discord members. -->
|
|
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
|
|
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
|
|
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span
|
|
x-data="counter(390)" x-text="counterValue">390</span>+</h4>
|
|
<p class="text-zinc-500 transition-colors group-hover:text-zinc-800">Fans on Discord</p>
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Day-2 problem -->
|
|
<section id="not-done" class="relative bg-zinc-900">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
|
|
<!-- Section heading -->
|
|
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-100 mb-4">
|
|
Deployed is <span class="marker-red">not done</span>
|
|
</h2>
|
|
<p class="text-lg text-zinc-400">
|
|
Deploying is the easy part. Production only starts when the deploy finishes.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="max-w-xl mx-auto">
|
|
<div class="rounded-2xl border border-zinc-700/60 bg-zinc-950 overflow-hidden">
|
|
|
|
<ul class="px-5 sm:px-8 py-6 space-y-4">
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0"
|
|
aria-hidden="true">
|
|
<svg class="w-4 h-4 text-green-400" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor" stroke-width="3"><path stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
d="M4.5 12.75l6 6 9-13.5"/></svg>
|
|
</span>
|
|
<span class="text-zinc-400">Machines provisioned</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0"
|
|
aria-hidden="true">
|
|
<svg class="w-4 h-4 text-green-400" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor" stroke-width="3"><path stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
d="M4.5 12.75l6 6 9-13.5"/></svg>
|
|
</span>
|
|
<span class="text-zinc-400">App built and deployed with
|
|
<code class="font-mono text-[13px] text-zinc-300 bg-zinc-800 border border-zinc-700 rounded px-1.5 py-0.5 whitespace-nowrap">uc deploy</code>
|
|
</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0"
|
|
aria-hidden="true">
|
|
<svg class="w-4 h-4 text-green-400" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor" stroke-width="3"><path stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
d="M4.5 12.75l6 6 9-13.5"/></svg>
|
|
</span>
|
|
<span class="text-zinc-400">Custom domain and HTTPS</span>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="relative">
|
|
<hr class="border-t border-dashed border-zinc-700" aria-hidden="true">
|
|
<span class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 whitespace-nowrap rounded-full border border-red-500/30 bg-[#200f11] px-3 py-1 font-inter-tight text-[11px] font-semibold uppercase tracking-widest text-red-400">
|
|
production starts here
|
|
</span>
|
|
</div>
|
|
|
|
<ul class="px-5 sm:px-8 pt-8 pb-6 space-y-4 bg-red-500/5">
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0 text-lg font-semibold text-red-400 leading-none"
|
|
aria-hidden="true">?</span>
|
|
<span class="text-zinc-100 font-medium">Services healthy right now?</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0 text-lg font-semibold text-red-400 leading-none"
|
|
aria-hidden="true">?</span>
|
|
<span class="text-zinc-100 font-medium">Logs you can search when something breaks?</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0 text-lg font-semibold text-red-400 leading-none"
|
|
aria-hidden="true">?</span>
|
|
<span class="text-zinc-100 font-medium">Alerts before users notice?</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0 text-lg font-semibold text-red-400 leading-none"
|
|
aria-hidden="true">?</span>
|
|
<span class="text-zinc-100 font-medium">Which deploy caused that error spike?</span>
|
|
</li>
|
|
<li class="flex items-center gap-3.5">
|
|
<span class="w-7 flex items-center justify-center shrink-0 text-lg font-semibold text-red-400 leading-none"
|
|
aria-hidden="true">?</span>
|
|
<span class="text-zinc-100 font-medium">Memory or CPU creeping up overnight?</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Handwritten bridge: an arrow from the note up into the open questions. -->
|
|
<p class="relative text-center mt-5">
|
|
<span class="hw-note inline-block -rotate-2 text-zinc-400">that's what Hub is for</span>
|
|
<svg class="absolute left-1/2 bottom-1 ml-[6rem] w-12 h-24 text-zinc-400"
|
|
width="48" height="96"
|
|
viewBox="0 0 48 96" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path d="M4 88c24-4 40-28 26-76M30 12l-2.5 8.5M30 12l6.5 6" stroke="currentColor"
|
|
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Dashboard overview -->
|
|
<section class="relative bg-white">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
|
|
Your cluster, at a glance
|
|
</h2>
|
|
<p class="text-lg text-zinc-500">
|
|
Hub provides you with a comprehensive view of your cluster's health and activity.
|
|
</p>
|
|
</div>
|
|
<div class="grid md:grid-cols-[2fr_4.5fr] gap-10 md:gap-16 items-center">
|
|
<!-- Left: feature list -->
|
|
<div class="flex flex-col gap-8">
|
|
<div class="flex items-start gap-4">
|
|
<div class="w-11 h-11 rounded-xl bg-violet-100 flex items-center justify-center shrink-0">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="3" y="3" width="7" height="7"/>
|
|
<rect x="14" y="3" width="7" height="7"/>
|
|
<rect x="3" y="14" width="7" height="7"/>
|
|
<rect x="14" y="14" width="7" height="7"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-zinc-900 mb-1">Services</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">See what's running, where it's
|
|
deployed, and whether it's healthy. Across every machine.</p>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-start gap-4">
|
|
<div class="w-11 h-11 rounded-xl bg-violet-100 flex items-center justify-center shrink-0">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="2" y="2" width="20" height="8" rx="2"/>
|
|
<rect x="2" y="14" width="20" height="8" rx="2"/>
|
|
<line x1="6" y1="6" x2="6.01" y2="6"/>
|
|
<line x1="6" y1="18" x2="6.01" y2="18"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-zinc-900 mb-1">Machines</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">See all nodes, their status, and
|
|
what's running on each one. No terminal required.</p>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-start gap-4">
|
|
<div class="w-11 h-11 rounded-xl bg-violet-100 flex items-center justify-center shrink-0">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<circle cx="12" cy="12" r="4"/>
|
|
<line x1="4.93" y1="4.93" x2="9.17" y2="9.17"/>
|
|
<line x1="14.83" y1="14.83" x2="19.07" y2="19.07"/>
|
|
<line x1="14.83" y1="9.17" x2="19.07" y2="4.93"/>
|
|
<line x1="4.93" y1="19.07" x2="9.17" y2="14.83"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-zinc-900 mb-1">Clusters</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">Manage multiple clusters from one
|
|
place. Switch between your dev and production clusters in one click.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Right: dashboard screenshot -->
|
|
<div>
|
|
<div class="rounded-2xl overflow-hidden border border-zinc-200 shadow-xl shadow-zinc-950/10">
|
|
<img src="images/hub-overview.webp" width="2560" height="1742"
|
|
alt="Uncloud Hub cluster overview dashboard, design preview" class="w-full block">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Value stack -->
|
|
<section class="relative bg-zinc-50">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
|
|
Observability stack for builders who'd rather be shipping
|
|
</h2>
|
|
<p class="text-lg text-zinc-500">
|
|
We host a Grafana and VictoriaMetrics stack for you so you don't need to build and monitor
|
|
the monitoring.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="max-w-sm mx-auto md:max-w-none grid gap-5 md:grid-cols-2 lg:grid-cols-3">
|
|
|
|
<!-- HTTP metrics -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">CPU, memory, disk, and HTTP
|
|
request metrics</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Monitor resource trends, response times, error rates, and traffic patterns for every
|
|
machine and service.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Custom app metrics -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="18" y1="20" x2="18" y2="10"/>
|
|
<line x1="12" y1="20" x2="12" y2="4"/>
|
|
<line x1="6" y1="20" x2="6" y2="14"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Custom app metrics</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Expose a Prometheus-style <code class="font-mono text-zinc-700">/metrics</code> endpoint
|
|
or push OpenTelemetry metrics from your app. Graph signups and failed jobs next to HTTP
|
|
requests and CPU.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Slow queries -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<ellipse cx="12" cy="5" rx="9" ry="3"/>
|
|
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
|
|
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Slow Postgres queries</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Find which queries are dragging down response times. Sorted by impact so you know where
|
|
to look first, not a wall of query logs.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Logs -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
|
<polyline points="14 2 14 8 20 8"/>
|
|
<line x1="16" y1="13" x2="8" y2="13"/>
|
|
<line x1="16" y1="17" x2="8" y2="17"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Searchable service and
|
|
system logs</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Filter by service, level, or time range. Jump from an anomaly in your metrics straight
|
|
to the logs from that exact minute without switching tools.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Pre-built dashboards -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="3" y="3" width="7" height="7"/>
|
|
<rect x="14" y="3" width="7" height="7"/>
|
|
<rect x="3" y="14" width="7" height="7"/>
|
|
<rect x="14" y="14" width="7" height="7"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Pre-built and custom
|
|
dashboards</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Start with the ready-to-use service and machine dashboards. Tweak them or build your own
|
|
Grafana dashboards.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Alerts -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Alerts on what
|
|
affects users</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3">
|
|
Get notified via Slack, email, or PagerDuty when error rates spike or latency crosses
|
|
your threshold. Detect, diagnose, and resolve an issue before a user reports it.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- API and AI agent access: full-width closing card. -->
|
|
<div class="p-6 rounded-xl border border-zinc-200 bg-white md:col-span-2 lg:col-span-3 md:flex md:items-start md:gap-5">
|
|
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center mb-4 md:mb-0 shrink-0">
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="4 17 10 11 4 5"/>
|
|
<line x1="12" y1="19" x2="20" y2="19"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">API and AI agent
|
|
access</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed mb-3 max-w-2xl">
|
|
Metrics and logs are queryable via API and CLI using standard PromQL and LogsQL.
|
|
Build your own tooling on top, or point an AI agent at an incident and have it
|
|
investigate the root cause for you.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- How it works -->
|
|
<section id="how-it-works" class="relative bg-white">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
|
|
How it works
|
|
</h2>
|
|
<p class="text-lg text-zinc-500">
|
|
Get started in three simple steps.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="max-w-3xl mx-auto grid gap-8 md:grid-cols-3 md:gap-6">
|
|
<div class="text-center md:text-left">
|
|
<div class="font-inter-tight text-3xl font-bold text-violet-600 mb-3">1</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Connect your cluster</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">
|
|
Run <code
|
|
class="font-mono text-[13px] text-zinc-700 bg-zinc-100 border border-zinc-200 rounded px-1.5 py-0.5 whitespace-nowrap">uc
|
|
hub connect</code> on the
|
|
cluster you already have. Its machines and services will appear in the dashboard. Your
|
|
deploy flow doesn't change.
|
|
<br>
|
|
No cluster yet?
|
|
<a class="underline hover:text-zinc-900"
|
|
href="/docs/getting-started/deploy-demo-app">Create your first cluster</a>
|
|
on any server in a few minutes.
|
|
</p>
|
|
</div>
|
|
<div class="text-center md:text-left">
|
|
<div class="font-inter-tight text-3xl font-bold text-violet-600 mb-3">2</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Deploy the agent</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">
|
|
Deploy the Grafana Alloy agent with
|
|
<code class="font-mono text-[13px] text-zinc-700 bg-zinc-100 border border-zinc-200 rounded px-1.5 py-0.5 whitespace-nowrap">uc
|
|
alloy deploy</code> or from
|
|
Hub. It configures the required exporters and collectors for you. Metrics and logs will
|
|
start flowing.
|
|
</p>
|
|
</div>
|
|
<div class="text-center md:text-left">
|
|
<div class="font-inter-tight text-3xl font-bold text-violet-600 mb-3">3</div>
|
|
<h3 class="font-inter-tight font-semibold text-zinc-900 mb-2">Monitor everything</h3>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">
|
|
Explore your cluster, services, metrics, and logs in one place. See how each deploy
|
|
behaves in production, and receive alerts when something needs attention.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- No lock-in note -->
|
|
<div class="max-w-3xl mx-auto mt-12 md:mt-14 pt-8 border-t border-zinc-200">
|
|
<p class="text-sm text-zinc-500 leading-relaxed">
|
|
<span class="font-semibold text-zinc-900">Disconnect Hub anytime, no lock-in.</span>
|
|
Uncloud is Apache 2.0 and fully works without Hub. Your apps run on your servers and
|
|
deploys keep working from the CLI. Disconnecting only removes Hub's dashboards and stored
|
|
metrics and logs.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Roadmap -->
|
|
<section class="relative bg-zinc-50">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-2xl mx-auto">
|
|
|
|
<div class="text-center pb-10 md:pb-14">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">Where Hub
|
|
goes next</h2>
|
|
<p class="text-lg text-zinc-500">Observability is chapter one. Already on the list:</p>
|
|
</div>
|
|
|
|
<ul>
|
|
<li class="py-5 border-b border-zinc-200 flex items-start gap-4">
|
|
<span class="w-2 h-2 rounded-full bg-violet-600 shrink-0 mt-2"
|
|
aria-hidden="true"></span>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-zinc-900 mb-1">Team access</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">Share cluster access with your
|
|
team using granular roles without handing out SSH keys or root credentials.</p>
|
|
</div>
|
|
</li>
|
|
<li class="py-5 border-b border-zinc-200 flex items-start gap-4">
|
|
<span class="w-2 h-2 rounded-full bg-violet-600 shrink-0 mt-2"
|
|
aria-hidden="true"></span>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-zinc-900 mb-1">Push to deploy</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">Deploy applications from your Git
|
|
repository automatically.</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<a href="#early-access"
|
|
class="group flex items-start gap-4 mt-6 border-[1.5px] border-dashed border-violet-300 hover:border-violet-600 rounded-xl p-5 sm:px-6 bg-violet-50/60 hover:bg-violet-100/60 transition-colors">
|
|
<svg class="w-5 h-5 shrink-0 mt-0.5 text-violet-600" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
|
<path d="M12 5v14M5 12h14"/>
|
|
</svg>
|
|
<div>
|
|
<p class="font-inter-tight font-semibold text-violet-700 mb-1">Your most-needed
|
|
feature</p>
|
|
<p class="text-sm text-zinc-500 leading-relaxed">
|
|
Something missing from this list? Tell us about your pain.</p>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- FAQ -->
|
|
<section class="relative bg-white">
|
|
<div class="py-12 md:py-20">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-2xl mx-auto">
|
|
|
|
<div class="text-center pb-10 md:pb-14">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">Fair
|
|
questions</h2>
|
|
<p class="text-lg text-zinc-500">
|
|
Have another questions? Ask us on
|
|
<a class="font-medium text-zinc-600 underline decoration-zinc-300 underline-offset-2 hover:text-zinc-900 hover:decoration-zinc-400 transition-colors"
|
|
href="https://uncloud.run/discord" target="_blank" rel="noopener">Discord</a>.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="divide-y divide-zinc-200 border-t border-b border-zinc-200">
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>Do I need Hub to use Uncloud?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
No. <a class="underline hover:text-zinc-900" href="/">Uncloud</a> is open source
|
|
and fully functional on its own without Hub. You can use the CLI to manage your
|
|
clusters and deploy your apps across servers from Compose files. You keep full
|
|
control over your servers. Hub adds a web UI and an observability stack on top so
|
|
you don't have to build and manage one yourself.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>Is Hub open source too?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
No, and no point pretending otherwise. Uncloud, the orchestrator, is Apache 2.0. Hub
|
|
is a managed service built on top, and it's how we plan to fund Uncloud's full-time
|
|
development.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>What data leaves my machines?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
Metrics and logs will flow to Hub. Storing, graphing, and searching them is the
|
|
whole point of a hosted observability stack so you can access them even when your
|
|
servers aren't available. Your apps, code, and databases stay on your servers. What
|
|
exactly gets collected and how you control it is being designed right now, with
|
|
early access users.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>What access does Hub get to my cluster?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
The connection model is being designed right now with early access users. The intent
|
|
is the minimum Hub needs to do its job: telemetry flowing out, and nothing
|
|
controlling your cluster unless you later opt into features like sharing access to
|
|
the cluster with your team or push to deploy. Uncloud itself never stops working
|
|
without Hub.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>What happens to my apps if Hub goes down?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
Nothing. Your apps run on your servers and Uncloud doesn't depend on Hub to
|
|
operate. Worst case, you lose the dashboard for a while, not production.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>Why not just run my own Grafana stack?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
You can and if you do already, keep it. Grafana, Prometheus, VictoriaMetrics and
|
|
others are excellent, and they'll always work with Uncloud. Hub is for the day you'd
|
|
rather ship features than manage and monitor the monitoring stack.
|
|
</p>
|
|
</details>
|
|
|
|
<details class="group">
|
|
<summary
|
|
class="flex items-center justify-between gap-4 py-5 cursor-pointer list-none [&::-webkit-details-marker]:hidden font-inter-tight font-semibold text-zinc-900">
|
|
<span>When can I use it, and what will it cost?</span>
|
|
<svg class="w-5 h-5 shrink-0 text-zinc-400 transition-transform duration-200 group-open:rotate-45"
|
|
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
|
|
aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m-7-7h14"/>
|
|
</svg>
|
|
</summary>
|
|
<p class="text-zinc-500 leading-relaxed pb-5">
|
|
Hub is in active development and everything on this page is a design preview.
|
|
Invites go to the early access list as pieces become usable. Pricing isn't decided
|
|
yet, and early access users will get a discount for helping shape Hub.
|
|
</p>
|
|
</details>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Final CTA: early access -->
|
|
<section id="early-access" class="relative bg-zinc-900">
|
|
<div class="py-16 md:py-24">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<div class="max-w-lg mx-auto">
|
|
|
|
<div class="text-center pb-10">
|
|
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-white mb-4">
|
|
Get early access
|
|
</h2>
|
|
<p class="text-lg text-zinc-400">
|
|
Leave your email and we'll invite you as Hub becomes usable. Early users help shape it
|
|
and get a discount.
|
|
</p>
|
|
</div>
|
|
|
|
<form id="hub-early-access-form">
|
|
<div class="mb-4">
|
|
<label for="hub-email" class="block text-sm font-medium text-zinc-300 mb-1.5">Your
|
|
email</label>
|
|
<input type="email" name="email" id="hub-email" required
|
|
placeholder="you@example.com"
|
|
class="form-input w-full bg-white border-zinc-700 rounded-lg px-4 py-3 text-sm text-zinc-900">
|
|
</div>
|
|
<div class="mb-6">
|
|
<label for="hub-message" class="block text-sm font-medium text-zinc-300 mb-1.5">
|
|
What will you use Hub for? What features would you like to see?
|
|
<span class="text-zinc-500 font-normal">(optional)</span>
|
|
</label>
|
|
<textarea name="message" id="hub-message" rows="3"
|
|
placeholder="e.g. searchable logs across all my services, or alerts when something goes down..."
|
|
class="form-textarea w-full bg-white border-zinc-700 rounded-lg px-4 py-3 text-sm text-zinc-900 resize-none"></textarea>
|
|
</div>
|
|
<!-- Disabled until PostHog which processes the submissions is loaded. -->
|
|
<button type="submit" disabled
|
|
class="btn text-zinc-900 bg-white hover:bg-zinc-100 w-full shadow font-semibold disabled:opacity-70">
|
|
Request early access
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Error shown below the form when submissions can't be sent, e.g. PostHog can't be loaded. -->
|
|
<p id="hub-form-error" class="hidden text-center text-sm text-red-400 mt-4">
|
|
Sorry, the form isn't working. It may be blocked by a browser extension.<br>
|
|
Please email
|
|
<a href="mailto:pasha@uncloud.run"
|
|
class="underline hover:text-red-300 transition">pasha@uncloud.run</a>
|
|
or message @psviderski on
|
|
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
|
|
class="underline hover:text-red-300 transition">Discord</a>
|
|
instead.
|
|
</p>
|
|
|
|
<!-- Success message shown in place of the form after submission. -->
|
|
<div id="hub-form-success" class="hidden text-center bg-zinc-800 rounded-lg px-6 py-8">
|
|
<p class="text-lg font-semibold text-white">You're on the list!</p>
|
|
<p class="text-zinc-400 mt-2">
|
|
We'll email you an invite as Hub becomes usable. Thanks for helping shape it.
|
|
</p>
|
|
<p class="text-zinc-400 mt-2">
|
|
Meanwhile,
|
|
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
|
|
class="underline text-zinc-300 hover:text-white transition">join our Discord</a>
|
|
to follow the development and chat about what you'd like Hub to do.
|
|
</p>
|
|
</div>
|
|
|
|
<p class="text-center text-sm text-zinc-500 mt-6">
|
|
Uncloud updates only, no spam. Or email
|
|
<a href="mailto:pasha@uncloud.run"
|
|
class="underline text-zinc-400 hover:text-white transition">pasha@uncloud.run</a>
|
|
directly.
|
|
</p>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
|
|
<!-- Site footer -->
|
|
<footer>
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
|
<!-- Top area: Blocks -->
|
|
<div class="grid sm:grid-cols-12 gap-8 py-8 md:py-12 border-t border-zinc-200">
|
|
<!-- 1st block - Brand -->
|
|
<div class="sm:col-span-6 md:col-span-6 lg:col-span-8 max-md:order-1 flex flex-col">
|
|
<div class="mb-4">
|
|
<!-- Logo -->
|
|
<a class="flex items-center" href="/">
|
|
<img class="w-8 h-8 rounded shadow-sm shadow-zinc-950/20" src="./images/logo.svg"
|
|
alt="Logo">
|
|
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
|
|
</a>
|
|
</div>
|
|
<div class="grow text-sm text-zinc-500">
|
|
Made with ❤️ by
|
|
<a class="underline hover:text-zinc-900" href="https://github.com/unlabs-dev">Unlabs</a>
|
|
in Australia
|
|
</div>
|
|
|
|
<!-- Social links -->
|
|
<ul class="flex space-x-4 mt-4 mb-1">
|
|
<li>
|
|
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
|
|
href="https://discord.gg/eR35KQJhPu" aria-label="Discord">
|
|
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
|
width="20"
|
|
height="20">
|
|
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
|
|
href="https://x.com/psviderski" aria-label="X/Twitter">
|
|
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
<path d="m7.063 3 3.495 4.475L14.601 3h2.454l-5.359 5.931L18 17h-4.938l-3.866-4.893L4.771 17H2.316l5.735-6.342L2 3h5.063Zm-.74 1.347H4.866l8.875 11.232h1.36L6.323 4.347Z"/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
|
|
href="https://t.me/spyyy" aria-label="Telegram">
|
|
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
<path d="M17.968 3.276a.338.338 0 0 0-.232-.253 1.192 1.192 0 0 0-.63.045S3.087 8.106 2.286 8.664c-.172.121-.23.19-.259.272-.138.4.293.573.293.573l3.613 1.177a.388.388 0 0 0 .183-.011c.822-.519 8.27-5.222 8.7-5.38.068-.02.118 0 .1.049-.172.6-6.606 6.319-6.64 6.354a.138.138 0 0 0-.05.118l-.337 3.528s-.142 1.1.956 0a30.66 30.66 0 0 1 1.9-1.738c1.242.858 2.58 1.806 3.156 2.3a1 1 0 0 0 .732.283.825.825 0 0 0 .7-.622S17.894 5.292 17.98 3.909c.008-.135.021-.217.021-.317a1.177 1.177 0 0 0-.032-.316Z"/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
|
|
href="https://github.com/psviderski/uncloud" aria-label="GitHub">
|
|
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
|
width="20"
|
|
height="20">
|
|
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- 2nd block - Documentation -->
|
|
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
|
|
|
<h6 class="text-sm text-zinc-800 font-medium mb-2">Documentation</h6>
|
|
<ul class="text-sm space-y-2">
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="/docs/#use-cases">Use cases</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="/docs/getting-started/deploy-demo-app">Quick start</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="/blog">Blog</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- 3rd block - Resources -->
|
|
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
|
<h6 class="text-sm text-zinc-800 font-medium mb-2">Resources</h6>
|
|
<ul class="text-sm space-y-2">
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="https://github.com/psviderski/uncloud">GitHub project</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="https://github.com/psviderski/uncloud/releases">Releases</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="https://github.com/psviderski/uncloud/blob/main/LICENSE">License</a>
|
|
</li>
|
|
<li>
|
|
<a class="text-zinc-500 hover:text-zinc-900 transition"
|
|
href="https://discord.gg/eR35KQJhPu">Community</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
|
|
<script src="./js/vendors/alpinejs.min.js" defer></script>
|
|
<script>
|
|
(function () {
|
|
// Fetch the GitHub stars count. Resolves to the live number or null if the request fails.
|
|
const starsPromise = fetch('https://api.github.com/repos/psviderski/uncloud')
|
|
.then(function (res) {
|
|
if (!res.ok) throw new Error('GitHub API ' + res.status);
|
|
return res.json();
|
|
})
|
|
.then(function (data) {
|
|
return typeof data.stargazers_count === 'number' ? data.stargazers_count : null;
|
|
})
|
|
.catch(function () {
|
|
return null;
|
|
});
|
|
|
|
starsPromise.then(function (count) {
|
|
if (count === null) return;
|
|
document.querySelectorAll('.js-github-stars').forEach(function (el) {
|
|
el.textContent = formatStars(count);
|
|
});
|
|
});
|
|
|
|
function formatStars(count) {
|
|
if (count < 1000) return String(count);
|
|
// Round to one decimal on the nearest 100 (e.g. 5250 -> 5.3k), matching GitHub's rounding.
|
|
return Math.round(count / 100) / 10 + 'k';
|
|
}
|
|
|
|
// Stats counter target for GitHub stars, in thousands.
|
|
window.githubStarsK = starsPromise.then(function (count) {
|
|
return count === null ? null : Math.round(count / 100) / 10;
|
|
});
|
|
|
|
// Fetch total GitHub downloads via the shields.io badge endpoint.
|
|
// Resolves to the absolute count, or null if the request fails.
|
|
const downloadsPromise = fetch('https://img.shields.io/github/downloads/psviderski/uncloud/total.json')
|
|
.then(function (res) {
|
|
if (!res.ok) throw new Error('shields.io ' + res.status);
|
|
return res.json();
|
|
})
|
|
.then(function (data) {
|
|
// data.value is a formatted string like "21k", "1.2M", or "950".
|
|
const m = /^([\d.]+)\s*([kM]?)/.exec(data.value || '');
|
|
if (!m) return null;
|
|
return parseFloat(m[1]) * {'': 1, k: 1e3, M: 1e6}[m[2]];
|
|
})
|
|
.catch(function () {
|
|
return null;
|
|
});
|
|
|
|
// Stats counter target for total downloads, in thousands.
|
|
window.downloadsK = downloadsPromise.then(function (total) {
|
|
return total === null ? null : Math.round(total / 1000);
|
|
});
|
|
|
|
// Animate counter.
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('counter', (target = 0, duration = 2000) => ({
|
|
startTimestamp: null,
|
|
step: null,
|
|
rawValue: 0,
|
|
counterValue: 0,
|
|
target: target,
|
|
precision: 0,
|
|
animationRequestId: null,
|
|
animationCompleted: false,
|
|
observer: null,
|
|
fallback: 0,
|
|
init() {
|
|
// Capture the static value rendered in the markup as the fallback.
|
|
this.fallback = parseFloat(this.$el.textContent)
|
|
// Intersection observer to watch visibility.
|
|
this.observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
// Check if element is in view.
|
|
if (entry.isIntersecting && !this.animationCompleted) {
|
|
this.startAnimation()
|
|
}
|
|
})
|
|
})
|
|
this.observer.observe(this.$el)
|
|
},
|
|
startAnimation() {
|
|
this.observer && this.observer.disconnect() // Animate once, stop further intersection callbacks.
|
|
Promise.resolve(this.target).then((target) => {
|
|
// Fall back to the markup value when failed to fetch the up-to-date value.
|
|
if (!Number.isFinite(target)) target = this.fallback
|
|
this.precision = (target % 1 === 0) ? 0 : (target.toString().split('.')[1] || '').length
|
|
// Skip the animation entirely for users who prefer reduced motion.
|
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
this.counterValue = target.toFixed(this.precision)
|
|
this.animationCompleted = true
|
|
return
|
|
}
|
|
this.step = (timestamp) => {
|
|
if (!this.startTimestamp) this.startTimestamp = timestamp
|
|
const progress = Math.min((timestamp - this.startTimestamp) / duration, 1)
|
|
const easedProgress = this.easeOut(progress)
|
|
this.rawValue = parseFloat((easedProgress * target).toFixed(this.precision))
|
|
this.counterValue = this.rawValue.toFixed(this.precision)
|
|
if (progress < 1) {
|
|
this.animationRequestId = window.requestAnimationFrame(this.step)
|
|
} else {
|
|
this.animationCompleted = true
|
|
}
|
|
}
|
|
this.animationRequestId = window.requestAnimationFrame(this.step)
|
|
})
|
|
},
|
|
easeOut(t) {
|
|
return 1 - Math.pow(1 - t, 5)
|
|
},
|
|
destroy() {
|
|
// Detach the handler to avoid memory and side-effect leakage.
|
|
this.animationRequestId && window.cancelAnimationFrame(this.step)
|
|
this.observer && this.observer.disconnect()
|
|
},
|
|
}))
|
|
});
|
|
|
|
// Send early access form submissions to PostHog and show an inline success message.
|
|
const form = document.getElementById('hub-early-access-form');
|
|
const formSuccess = document.getElementById('hub-form-success');
|
|
const formError = document.getElementById('hub-form-error');
|
|
const submitButton = form.querySelector('button[type="submit"]');
|
|
|
|
// The submit button starts disabled and is enabled once PostHog is ready to accept submissions.
|
|
// If it doesn't load in time, show an error but keep polling in case it loads late.
|
|
const posthogDeadline = Date.now() + 5000;
|
|
(function enableFormWhenPostHogLoads() {
|
|
if (window.posthog && window.posthog.__loaded) {
|
|
submitButton.disabled = false;
|
|
formError.classList.add('hidden');
|
|
return;
|
|
}
|
|
if (Date.now() > posthogDeadline) {
|
|
formError.classList.remove('hidden');
|
|
}
|
|
setTimeout(enableFormWhenPostHogLoads, 200);
|
|
})();
|
|
|
|
form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
const email = form.elements.email.value.trim();
|
|
if (!email) return;
|
|
try {
|
|
// Key the person by email so the early access list shows up under People in PostHog.
|
|
posthog.identify(email, {email: email});
|
|
posthog.capture('hub_early_access_requested', {
|
|
email: email,
|
|
message: form.elements.message.value.trim(),
|
|
});
|
|
} catch (err) {
|
|
console.error('Failed to send the form:', err);
|
|
formError.classList.remove('hidden');
|
|
return;
|
|
}
|
|
form.classList.add('hidden');
|
|
formSuccess.classList.remove('hidden');
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|