Files
VectorDNS/app/docs/architecture/page.tsx
Aiden Smith 301402e2b1 Add documentation pages with interactive architecture diagram
- /docs route with sidebar navigation and index page
- Architecture section: system overview with React Flow diagram, data flow, database schema
- API reference, configuration guide, and self-hosting docs for Go DNS server
- Feature planning document for future development
- Added docs link to landing page nav
2026-02-24 18:23:09 -05:00

216 lines
6.9 KiB
TypeScript

import type { Metadata } from "next";
import { Layers, Globe, Server, Database } from "lucide-react";
import { ArchitectureDiagram } from "@/components/docs/architecture-diagram";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
export const metadata: Metadata = {
title: "System Overview",
};
const services = [
{
icon: Globe,
title: "Next.js (Vercel)",
badge: "Frontend",
badgeVariant: "secondary" as const,
items: [
"All UI rendering (SSR + client)",
"Authentication via Supabase",
"WHOIS lookups (whoiser library)",
"Domain availability checks (IANA RDAP)",
"Dashboard, notifications, settings pages",
"Proxies DNS queries to the Go service",
],
},
{
icon: Server,
title: "Go Microservice (VPS)",
badge: "DNS API",
badgeVariant: "outline" as const,
items: [
"DNS record lookups via miekg/dns (UDP/TCP, not DoH)",
"Query specific or authoritative nameservers directly",
"DNSSEC validation",
"DNS propagation checking across multiple resolvers",
"Scheduled monitoring (native cron, no serverless time limits)",
"Change detection (diff DNS snapshots, notify on changes)",
],
},
{
icon: Database,
title: "Supabase",
badge: "Database & Auth",
badgeVariant: "secondary" as const,
items: [
"Managed Postgres database",
"Auth (OAuth + email/password + magic link)",
"Row Level Security (RLS) for data isolation",
],
},
];
const tradeoffs = [
{
concern: "Frontend hosting, SSR, auth",
solution: "Vercel (serverless, zero-ops)",
},
{
concern: "DNS resolution, monitoring",
solution: "Go on VPS (persistent process, no cold starts)",
},
{
concern: "Database, auth state",
solution: "Supabase (managed Postgres)",
},
];
const goAdvantages = [
"Direct UDP/TCP DNS queries — faster, no middleman",
"Can query authoritative nameservers directly",
"Supports DNSSEC validation, AXFR, propagation checks",
"No cold starts, consistent latency",
"No Vercel function timeout limits for monitoring jobs",
];
export default function ArchitectureOverviewPage() {
return (
<div className="space-y-10">
{/* Page header */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<Layers className="size-6 text-primary" />
<h1 className="text-3xl font-bold tracking-tight">System Overview</h1>
</div>
<p className="text-lg text-muted-foreground">
VectorDNS uses a hybrid architecture: a Next.js frontend on Vercel and
a Go DNS microservice on a VPS, backed by Supabase for auth and
storage.
</p>
</div>
<Separator />
{/* Architecture diagram */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
Architecture Diagram
</h2>
<ArchitectureDiagram />
</div>
{/* Services */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
What Each Service Handles
</h2>
<div className="grid gap-4 md:grid-cols-3">
{services.map((service) => (
<Card key={service.title}>
<CardHeader className="pb-3">
<div className="flex items-start justify-between gap-2">
<service.icon className="mt-0.5 size-5 text-primary" />
<Badge variant={service.badgeVariant}>{service.badge}</Badge>
</div>
<CardTitle className="text-base">{service.title}</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-1.5 text-sm text-muted-foreground">
{service.items.map((item) => (
<li key={item} className="flex gap-2">
<span className="mt-1.5 size-1 shrink-0 rounded-full bg-primary" />
{item}
</li>
))}
</ul>
</CardContent>
</Card>
))}
</div>
</div>
<Separator />
{/* Why hybrid */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">Why Hybrid?</h2>
<Card>
<CardContent className="pt-6">
<div className="divide-y">
{tradeoffs.map(({ concern, solution }) => (
<div
key={concern}
className="flex flex-col gap-1 py-3 first:pt-0 last:pb-0 sm:flex-row sm:items-center sm:gap-4"
>
<span className="min-w-55 text-sm font-medium">
{concern}
</span>
<span className="text-sm text-muted-foreground">
{solution}
</span>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* Communication */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">Communication</h2>
<p className="text-sm text-muted-foreground">
Next.js API routes call the Go service over HTTPS. The Go service URL
is configured via{" "}
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">
GO_DNS_API_URL
</code>{" "}
and requests are authenticated with a shared API key via{" "}
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">
GO_DNS_API_KEY
</code>
.
</p>
<Card>
<CardContent className="p-0">
<pre className="overflow-x-auto bg-muted/50 p-4 font-mono text-sm text-foreground">
Next.js API route HTTPS Go DNS API UDP/TCP DNS resolvers
</pre>
</CardContent>
</Card>
</div>
{/* Why Go over DoH */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
Why Go Over DoH?
</h2>
<Card>
<CardHeader className="pb-3">
<CardDescription>
VectorDNS uses a custom Go microservice for DNS resolution instead
of DNS-over-HTTPS providers like Cloudflare&apos;s Tangerine.
</CardDescription>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground">
{goAdvantages.map((item) => (
<li key={item} className="flex gap-2">
<span className="mt-1.5 size-1 shrink-0 rounded-full bg-primary" />
{item}
</li>
))}
</ul>
</CardContent>
</Card>
</div>
</div>
);
}