Skip to content

Commit 8903aea

Browse files
committed
Add privacy+terms and improve ui
1 parent cb87e01 commit 8903aea

7 files changed

Lines changed: 360 additions & 5 deletions

File tree

apps/www/astro.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
// @ts-check
22
import { defineConfig } from 'astro/config';
3+
import sitemap from "@astrojs/sitemap";
34
import tailwindcss from "@tailwindcss/vite";
45

56
// https://astro.build/config
67
export default defineConfig({
8+
site: "https://livedot.dev",
9+
integrations: [sitemap()],
710
vite: {
811
plugins: [tailwindcss()],
912
},

apps/www/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"astro": "^6.0.8"
1717
},
1818
"devDependencies": {
19+
"@astrojs/sitemap": "^3.7.1",
1920
"@tailwindcss/vite": "^4.2.2",
2021
"tailwindcss": "^4.2.2"
2122
}

apps/www/src/components/Footer.astro

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
---
22
const footerLinks = [
33
{ label: "Docs", href: "https://github.com/mxvsh/livedot" },
4+
{ label: "Privacy", href: "/privacy" },
5+
{ label: "Terms", href: "/terms" },
46
{ label: "X", href: "https://x.com/monawwarx" },
57
{ label: "Discord", href: "http://mxv.sh/discord" },
68
];
79
---
810

9-
<footer class="w-full border-t border-surface-variant/30 bg-surface">
10-
<div class="mx-auto flex max-w-7xl flex-col items-center justify-between gap-8 px-6 py-12 md:flex-row">
11+
<footer class="relative w-full overflow-hidden border-t border-surface-variant/30 bg-surface">
12+
<div class="relative z-10 mx-auto flex max-w-7xl flex-col items-center justify-between gap-8 px-6 py-12 md:flex-row">
1113
<div class="flex flex-col items-center gap-2 md:items-start">
12-
<div class="text-lg font-bold text-primary">Livedot</div>
14+
<a href="/" class="flex items-center gap-3 text-2xl font-bold text-primary">
15+
<img src="/logo.svg" alt="Livedot logo" class="h-8 w-8 rounded-md" />
16+
<span class="brand-wordmark">livedot</span>
17+
</a>
1318
<p class="text-sm tracking-wide text-on-surface-variant">
14-
© 2026 Livedot. Built for the obsidian pulse.
19+
© 2026 Livedot. Built for live traffic.
1520
</p>
1621
</div>
1722

apps/www/src/components/Header.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const navLinks = [
1818

1919
<header class="fixed inset-x-0 top-0 z-50 border-b border-white/5 bg-surface/80 backdrop-blur-md">
2020
<nav class="mx-auto flex h-20 max-w-7xl items-center justify-between px-6">
21-
<a href="/" class="flex items-center gap-3 text-lg font-bold text-primary md:text-xl">
21+
<a href="/" class="flex items-center gap-3 text-2xl font-bold text-primary">
2222
<img src="/logo.svg" alt="Livedot logo" class="h-8 w-8 rounded-md" />
2323
<span class="brand-wordmark">livedot</span>
2424
</a>

apps/www/src/pages/privacy.astro

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
import Footer from "../components/Footer.astro";
3+
import Header from "../components/Header.astro";
4+
import MainLayout from "../layouts/MainLayout.astro";
5+
6+
const pathname = Astro.url.pathname;
7+
const pageUrl = new URL(pathname, Astro.site ?? Astro.url).href;
8+
const homeUrl = new URL("/", Astro.site ?? Astro.url).href;
9+
10+
const structuredData = [
11+
{
12+
"@type": "WebPage",
13+
name: "Livedot Privacy Policy",
14+
description: "Read the Livedot privacy policy covering account data, visitor data, retention, and self-hosted instances.",
15+
url: pageUrl,
16+
isPartOf: {
17+
"@type": "WebSite",
18+
name: "Livedot",
19+
url: homeUrl,
20+
},
21+
},
22+
{
23+
"@type": "BreadcrumbList",
24+
itemListElement: [
25+
{ "@type": "ListItem", position: 1, name: "Home", item: homeUrl },
26+
{ "@type": "ListItem", position: 2, name: "Privacy Policy", item: pageUrl },
27+
],
28+
},
29+
];
30+
31+
const sections = [
32+
{
33+
title: "Overview",
34+
paragraphs: [
35+
"Livedot is a real-time website visitor tracking tool. We are committed to protecting your privacy and being transparent about the data we collect and how we use it.",
36+
],
37+
},
38+
{
39+
title: "Data We Collect",
40+
paragraphs: [
41+
"Account data: When you sign up, we collect your email address and a hashed password (or OAuth identity). We never store plaintext passwords.",
42+
"Visitor data: When the tracking script is installed on your website, we receive the visitor's approximate geographic location (derived from IP address), the page URL, and a session identifier. We do not store raw IP addresses.",
43+
"Usage data: We count events per month per user to enforce plan limits. This data is stored in aggregate and not tied to individual end-users.",
44+
],
45+
},
46+
{
47+
title: "Data We Do Not Collect",
48+
intro: "We do not collect, store, or process:",
49+
items: [
50+
"Raw IP addresses of your website visitors",
51+
"Cookies or persistent identifiers on your visitors' devices",
52+
"Personal information about your visitors (names, emails, etc.)",
53+
"Browsing history outside of your tracked pages",
54+
],
55+
},
56+
{
57+
title: "How We Use Your Data",
58+
paragraphs: [
59+
"Account data is used to authenticate you and manage your subscription. Visitor data is used solely to provide the real-time dashboard you see. We do not sell, share, or use your data for advertising.",
60+
],
61+
},
62+
{
63+
title: "Data Retention",
64+
paragraphs: [
65+
"Visitor session data is kept in memory and expires based on your plan (10 minutes on Free, 24 hours on Pro, 7 days on Max). After expiry the data is permanently deleted. Account data is retained until you delete your account.",
66+
],
67+
},
68+
{
69+
title: "Third-Party Services",
70+
intro: "We use the following third-party services:",
71+
items: [
72+
"Polar.sh: payment processing for paid plans. Subject to Polar's privacy policy.",
73+
"MaxMind GeoLite2: IP-to-location database, processed locally on our servers. No data is sent to MaxMind.",
74+
],
75+
},
76+
{
77+
title: "Self-Hosted Instances",
78+
paragraphs: [
79+
"If you self-host Livedot (Community Edition), all data stays on your own infrastructure. We have no access to any data on self-hosted instances.",
80+
],
81+
},
82+
{
83+
title: "Your Rights",
84+
paragraphs: [
85+
"You may request deletion of your account and associated data at any time by contacting us. We will process deletion requests within 30 days.",
86+
],
87+
},
88+
];
89+
---
90+
91+
<MainLayout
92+
title="Livedot Privacy Policy"
93+
description="Read the Livedot privacy policy covering account data, visitor data, retention, and self-hosted instances."
94+
pathname={pathname}
95+
structuredData={structuredData}
96+
>
97+
<Header pathname={pathname} />
98+
<main class="pt-20">
99+
<section class="mx-auto max-w-4xl px-6 py-24">
100+
<div class="grid-panel border border-white/8 bg-surface-container p-8 md:p-12">
101+
<a href="/" class="inline-flex items-center gap-2 text-xs uppercase tracking-[0.25em] text-on-surface-variant transition-colors hover:text-white">
102+
Back to home
103+
</a>
104+
<div class="mt-8 max-w-3xl">
105+
<p class="text-xs uppercase tracking-[0.32em] text-primary">Legal</p>
106+
<h1 class="mt-4 font-headline text-4xl font-bold tracking-[-0.06em] text-white md:text-6xl">Privacy Policy</h1>
107+
<p class="mt-4 text-sm text-on-surface-variant">Last updated: March 2026</p>
108+
<p class="mt-6 text-lg leading-8 text-on-surface-variant">
109+
Read how Livedot handles account data, visitor activity, retention, and self-hosted deployments.
110+
</p>
111+
</div>
112+
113+
<div class="mt-14 space-y-10">
114+
{sections.map((section) => (
115+
<section class="border-t border-white/8 pt-8">
116+
<h2 class="font-headline text-2xl font-bold tracking-[-0.04em] text-white">{section.title}</h2>
117+
{section.paragraphs?.map((paragraph) => (
118+
<p class="mt-4 text-base leading-7 text-on-surface-variant">{paragraph}</p>
119+
))}
120+
{section.intro && <p class="mt-4 text-base leading-7 text-on-surface-variant">{section.intro}</p>}
121+
{section.items && (
122+
<ul class="mt-4 space-y-3 text-base leading-7 text-on-surface-variant">
123+
{section.items.map((item) => (
124+
<li class="flex gap-3">
125+
<span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span>
126+
<span>
127+
{item.includes("Polar.sh") ? (
128+
<>
129+
Polar.sh: payment processing for paid plans. Subject to{" "}
130+
<a href="https://polar.sh/legal/privacy" target="_blank" rel="noreferrer" class="text-white underline underline-offset-2">
131+
Polar&apos;s privacy policy
132+
</a>.
133+
</>
134+
) : item}
135+
</span>
136+
</li>
137+
))}
138+
</ul>
139+
)}
140+
</section>
141+
))}
142+
143+
<section class="border-t border-white/8 pt-8">
144+
<h2 class="font-headline text-2xl font-bold tracking-[-0.04em] text-white">Contact</h2>
145+
<p class="mt-4 text-base leading-7 text-on-surface-variant">
146+
For privacy-related questions, contact us at{" "}
147+
<a href="mailto:privacy@livedot.dev" class="text-white underline underline-offset-2">privacy@livedot.dev</a>.
148+
</p>
149+
</section>
150+
</div>
151+
</div>
152+
</section>
153+
</main>
154+
<Footer />
155+
</MainLayout>

apps/www/src/pages/terms.astro

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
import Footer from "../components/Footer.astro";
3+
import Header from "../components/Header.astro";
4+
import MainLayout from "../layouts/MainLayout.astro";
5+
6+
const pathname = Astro.url.pathname;
7+
const pageUrl = new URL(pathname, Astro.site ?? Astro.url).href;
8+
const homeUrl = new URL("/", Astro.site ?? Astro.url).href;
9+
10+
const structuredData = [
11+
{
12+
"@type": "WebPage",
13+
name: "Livedot Terms of Service",
14+
description: "Read the Livedot terms covering accounts, billing, acceptable use, and service limitations.",
15+
url: pageUrl,
16+
isPartOf: {
17+
"@type": "WebSite",
18+
name: "Livedot",
19+
url: homeUrl,
20+
},
21+
},
22+
{
23+
"@type": "BreadcrumbList",
24+
itemListElement: [
25+
{ "@type": "ListItem", position: 1, name: "Home", item: homeUrl },
26+
{ "@type": "ListItem", position: 2, name: "Terms of Service", item: pageUrl },
27+
],
28+
},
29+
];
30+
31+
const sections = [
32+
{
33+
title: "Acceptance",
34+
paragraphs: [
35+
"By creating an account or using Livedot, you agree to these Terms of Service. If you do not agree, do not use the service.",
36+
],
37+
},
38+
{
39+
title: "The Service",
40+
paragraphs: [
41+
"Livedot provides real-time website visitor tracking. We offer a cloud-hosted service (cloud.livedot.dev) and an open-source self-hosted edition (Community Edition) available on GitHub.",
42+
],
43+
},
44+
{
45+
title: "Your Account",
46+
paragraphs: [
47+
"You are responsible for maintaining the security of your account credentials. You must not share your account or allow others to access it. You must provide accurate information when creating your account.",
48+
"We reserve the right to suspend or terminate accounts that violate these terms, engage in abuse, or use the service in a way that disrupts others.",
49+
],
50+
},
51+
{
52+
title: "Acceptable Use",
53+
intro: "You may not use Livedot to:",
54+
items: [
55+
"Track websites you do not own or have permission to track",
56+
"Violate any applicable laws or regulations",
57+
"Attempt to circumvent plan limits or access controls",
58+
"Resell or sublicense the service without our written permission",
59+
"Interfere with or disrupt the service or its infrastructure",
60+
],
61+
},
62+
{
63+
title: "Plans and Billing",
64+
paragraphs: [
65+
"Paid plans (Pro, Max) are billed monthly through Polar.sh. Payments are non-refundable except where required by law. You may cancel your subscription at any time; access continues until the end of the current billing period.",
66+
"We reserve the right to change pricing with 30 days notice. Continued use after a price change constitutes acceptance of the new pricing.",
67+
],
68+
},
69+
{
70+
title: "Plan Limits",
71+
paragraphs: [
72+
"Each plan includes a monthly event limit. Events exceeding your plan limit are silently dropped. We do not charge overages. If you consistently exceed your limit, consider upgrading.",
73+
],
74+
},
75+
{
76+
title: "Data and Privacy",
77+
paragraphs: [
78+
"Your use of the service is also governed by our Privacy Policy, which is incorporated into these terms by reference.",
79+
],
80+
},
81+
{
82+
title: "Uptime and Availability",
83+
paragraphs: [
84+
"We strive for high availability but do not guarantee any specific uptime SLA on the free plan. Pro and Max plans are given priority in the event of resource constraints. We are not liable for losses caused by downtime.",
85+
],
86+
},
87+
{
88+
title: "Intellectual Property",
89+
paragraphs: [
90+
"Livedot is open-source software licensed under the MIT License. The cloud service, branding, and domain are owned by Livedot. You retain all ownership of your data.",
91+
],
92+
},
93+
{
94+
title: "Disclaimer of Warranties",
95+
paragraphs: [
96+
"The service is provided \"as is\" without warranty of any kind. We disclaim all warranties, express or implied, including merchantability and fitness for a particular purpose.",
97+
],
98+
},
99+
{
100+
title: "Limitation of Liability",
101+
paragraphs: [
102+
"To the maximum extent permitted by law, Livedot shall not be liable for any indirect, incidental, or consequential damages arising from your use of the service. Our total liability shall not exceed the amount you paid us in the last 3 months.",
103+
],
104+
},
105+
{
106+
title: "Changes to Terms",
107+
paragraphs: [
108+
"We may update these terms from time to time. We will notify users of significant changes via email or in-app notice. Continued use after changes constitutes acceptance.",
109+
],
110+
},
111+
];
112+
---
113+
114+
<MainLayout
115+
title="Livedot Terms of Service"
116+
description="Read the Livedot terms covering accounts, billing, acceptable use, and service limitations."
117+
pathname={pathname}
118+
structuredData={structuredData}
119+
>
120+
<Header pathname={pathname} />
121+
<main class="pt-20">
122+
<section class="mx-auto max-w-4xl px-6 py-24">
123+
<div class="grid-panel border border-white/8 bg-surface-container p-8 md:p-12">
124+
<a href="/" class="inline-flex items-center gap-2 text-xs uppercase tracking-[0.25em] text-on-surface-variant transition-colors hover:text-white">
125+
Back to home
126+
</a>
127+
<div class="mt-8 max-w-3xl">
128+
<p class="text-xs uppercase tracking-[0.32em] text-primary">Legal</p>
129+
<h1 class="mt-4 font-headline text-4xl font-bold tracking-[-0.06em] text-white md:text-6xl">Terms of Service</h1>
130+
<p class="mt-4 text-sm text-on-surface-variant">Last updated: March 2026</p>
131+
<p class="mt-6 text-lg leading-8 text-on-surface-variant">
132+
Review the rules, billing terms, acceptable use boundaries, and liability limits that govern Livedot.
133+
</p>
134+
</div>
135+
136+
<div class="mt-14 space-y-10">
137+
{sections.map((section) => (
138+
<section class="border-t border-white/8 pt-8">
139+
<h2 class="font-headline text-2xl font-bold tracking-[-0.04em] text-white">{section.title}</h2>
140+
{section.paragraphs?.map((paragraph) => (
141+
<p class="mt-4 text-base leading-7 text-on-surface-variant">
142+
{section.title === "Data and Privacy" ? (
143+
<>
144+
Your use of the service is also governed by our{" "}
145+
<a href="/privacy" class="text-white underline underline-offset-2">Privacy Policy</a>, which is incorporated into these terms by reference.
146+
</>
147+
) : paragraph}
148+
</p>
149+
))}
150+
{section.intro && <p class="mt-4 text-base leading-7 text-on-surface-variant">{section.intro}</p>}
151+
{section.items && (
152+
<ul class="mt-4 space-y-3 text-base leading-7 text-on-surface-variant">
153+
{section.items.map((item) => (
154+
<li class="flex gap-3">
155+
<span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span>
156+
<span>{item}</span>
157+
</li>
158+
))}
159+
</ul>
160+
)}
161+
</section>
162+
))}
163+
164+
<section class="border-t border-white/8 pt-8">
165+
<h2 class="font-headline text-2xl font-bold tracking-[-0.04em] text-white">Contact</h2>
166+
<p class="mt-4 text-base leading-7 text-on-surface-variant">
167+
For questions about these terms, contact us at{" "}
168+
<a href="mailto:hello@livedot.dev" class="text-white underline underline-offset-2">hello@livedot.dev</a>.
169+
</p>
170+
</section>
171+
</div>
172+
</div>
173+
</section>
174+
</main>
175+
<Footer />
176+
</MainLayout>

0 commit comments

Comments
 (0)