- Changelog -
-- A record of every release, feature, and fix. -
++ Changelog +
++ {CHANGELOG_RELEASE_COUNT} releases shipped — every feature, + fix, and security improvement on the record. + {latest ? ( + <> + {" "} + Latest:{" "} + + v{latest.version} + {" "} + on{" "} + + {latest.date} + + . + > + ) : null} +
+-
- {entry.highlights.map((h, j) => (
-
- - - {h.type} - - - {h.text} - - - ))} -
- Getting Started -
-- Three steps to quantum-secured secrets: install, store, and - integrate. -
++ Getting Started +
++ Three steps to quantum-secured secrets: install, store, and + integrate. Then dive into the {CLI_REFERENCE_COUNT}-command CLI + reference and the MCP cookbook. +
+- - 1 - - Install q-ring -
-- Pick your package manager and install globally: -
-- ${" "} - {i.cmd} --
+ Pick your package manager and install globally: +
++ ${" "} + + {i.cmd} + ++
- - 2 - - Store your first secret -
-- Secrets are stored in your OS-native keyring (macOS Keychain, - Windows Credential Vault, or Linux Secret Service): -
-- # Store a secret - {"\n"} - ${" "} - qring set OPENAI_API_KEY sk-proj-abc123... - {"\n\n"} - # Retrieve it - {"\n"} - ${" "} - qring get OPENAI_API_KEY - {"\n"} - sk-proj-abc123... - {"\n\n"} - # List all stored keys - {"\n"} - ${" "} - qring list - {"\n"} - OPENAI_API_KEY [dev] healthy 0 reads - {"\n\n"} - # Run a health check - {"\n"} - ${" "} - qring health --
+ Secrets are stored in your OS-native keyring (macOS Keychain, + Windows Credential Vault, or Linux Secret Service): +
++ # Store a secret + {"\n"} + ${" "} + + qring set + {" "} + OPENAI_API_KEY sk-proj-abc123... + {"\n\n"} + # Retrieve it + {"\n"} + ${" "} + + qring get + {" "} + OPENAI_API_KEY + {"\n"} + + sk-proj-abc123... + + {"\n\n"} + # List all stored keys + {"\n"} + ${" "} + + qring list + + {"\n"} + + OPENAI_API_KEY [dev] healthy 0 reads + + {"\n\n"} + # Run a health check + {"\n"} + ${" "} + + qring health + ++
- - 3 - - Configure MCP -
-- Add q-ring as an MCP server so AI agents can manage secrets - natively. Just add one entry to your config: -
++ Add q-ring as an MCP server so AI agents can manage secrets + natively. Same single binary, every host. +
-- Cursor / Kiro -
-
- {`{
+
+
+
+
+ Cursor / Kiro
+
+
-
-
+ maxWidth="100%"
+ >
+ {`{
+ "mcpServers": {
+ "q-ring": {
+ "command": "qring-mcp"
+ }
+ }
+}`}
+
+
-
- Claude Code
-
-
-
- {`{
+
+
+
+ Claude Code
+
+
-
-
-
-
- {/* Step 4: Cursor Plugin */}
-
-
-
-
- 4
-
- Cursor Plugin
-
-
- The q-ring Cursor Plugin brings
- quantum secret management directly into your IDE. Install from the
- Cursor marketplace or copy manually:
-
-
-
-
-
- 3 Rules
- Always-on guidance for secret hygiene, q-ring workflow, and .env safety
-
-
- 4 Skills
- Auto-triggered: secret management, scanning, rotation, onboarding
-
-
- 2 Agents
- Security auditor and day-to-day secret ops assistant
-
-
- 5 Commands
- Scan, health-check, rotate, setup, and teleport
-
-
- 2 Hooks
- After file edit and session start automation
+ maxWidth="100%"
+ >
+ {`{
+ "mcpServers": {
+ "q-ring": {
+ "command": "qring-mcp"
+ }
+ }
+}`}
+
-
- MCP Connector
- Auto-connects to qring-mcp — all 44 tools available
+
+
+
+
+ VS Code
+
+
+ {`{
+ "servers": {
+ "q-ring": {
+ "command": "qring-mcp"
+ }
+ }
+}`}
+
-
-
- # From the quantum_ring repo
- {"\n"}
- ${" "}
- cp -r cursor-plugin/ ~/.cursor/plugins/qring/
-
-
-
-
+
+
+
+ The{" "}
+
+ q-ring Cursor Plugin
+ {" "}
+ bundles rules, skills, agents, slash commands, hooks, and the
+ MCP connector — pre-wired to the 44 tools.
+
-
-
- CLI Complete Reference
-
-
- Every CLI command and option is listed below with at least one real invocation example.
-
-
- {cliReference.map((item) => (
-
-
- qring {item.command}
-
- {item.description && (
-
- {item.description}
-
- )}
-
- Options
- {item.options.length === 0 ? (
- No command-specific options.
- ) : (
-
- {item.options.map((opt) => (
-
- {opt}
-
- ))}
+
+
+
+ {PLUGIN_PREVIEW.map((p) => (
+
+
+
+ {p.title}
+
+
+ {p.desc}
+
- )}
+ ))}
-
-
- {item.examples.map((ex, idx) => (
-
- ${" "}
- {ex}
- {idx < item.examples.length - 1 ? "\n" : ""}
-
- ))}
-
-
-
- ))}
+
+
+
+
+
+
+ # Install from Cursor marketplace, or manually:
+
+ {"\n"}
+ ${" "}
+
+ cp -r cursor-plugin/ ~/.cursor/plugins/qring/
+
+
+
+
-
-
- MCP Prompt Cookbook
-
-
- Every MCP tool in q-ring with a one-sentence prompt example you can paste into an agent chat.
-
-
- {mcpToolPrompts.map(([tool, prompt]) => (
-
-
-
- {tool}
-
-
- {prompt}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {MCP_PROMPTS.map(([tool, prompt]) => (
+
))}
+
-
-
-
+
+
+
+
Ready to explore more features?
-
+
- Explore Features
+ Explore features
- View Changelog
+
+ View changelog
+
+
+
+
+
+
>
);
}
+
+function SectionHeader({
+ Icon,
+ eyebrow,
+ title,
+ desc,
+}: {
+ Icon: React.ElementType;
+ eyebrow: string;
+ title: string;
+ desc: string;
+}) {
+ return (
+
+
+
+ {eyebrow}
+
+ {title}
+
+ {desc}
+
+
+ );
+}
+
+function SubHeading({ number, title }: { number: number; title: string }) {
+ return (
+
+
+ {number}
+
+ {title}
+
+ );
+}
diff --git a/web/app/globals.css b/web/app/globals.css
index 5f8b908..bfa1cfe 100644
--- a/web/app/globals.css
+++ b/web/app/globals.css
@@ -1,17 +1,20 @@
@import "tailwindcss";
+@import "@heroui/styles";
+@import "@heroui-pro/react/css";
@theme inline {
- --color-bg-deep: #04080f;
- --color-bg-section: #080e18;
- --color-bg-alt: #0b1221;
- --color-bg-card: #0f1a2e;
- --color-bg-card-hover: #142240;
+ /* q-ring legacy tokens (keep for WebGL / motion / existing utilities) */
+ --color-bg-deep: #020409;
+ --color-bg-section: #050a13;
+ --color-bg-alt: #070c19;
+ --color-bg-card: #0c1626;
+ --color-bg-card-hover: #111c36;
--color-border: #1a2d4a;
--color-border-glow: #0ea5e9;
--color-text-primary: #f8f8ff;
- --color-text-secondary: #c8cfe0;
- --color-text-dim: #8899b4;
+ --color-text-secondary: #d3dae9;
+ --color-text-dim: #94a3bd;
--color-accent: #0ea5e9;
--color-accent-bright: #38bdf8;
@@ -22,6 +25,23 @@
--color-warning: #fbbf24;
--color-green: #22c55e;
+ /* HeroUI semantic tokens (Quantum Fluidity / dark) */
+ --color-background: #020409;
+ --color-foreground: #f8f8ff;
+ --color-muted: #94a3bd;
+ --color-surface: #0c1626;
+ --color-surface-foreground: #f8f8ff;
+ --color-overlay: #111c36;
+ --color-default: #070c19;
+ --color-default-foreground: #d3dae9;
+ --color-separator: #1a2d4a;
+ --color-accent-foreground: #020409;
+ --color-success: #22c55e;
+ --color-success-foreground: #020409;
+ --color-warning-foreground: #020409;
+ --color-danger-foreground: #020409;
+ --color-focus: #0ea5e9;
+
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 20px;
@@ -96,7 +116,7 @@
content: "";
position: absolute;
inset: 0;
- background: rgba(4, 8, 15, 0.55);
+ background: rgba(2, 4, 9, 0.72);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
z-index: 1;
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
index 5cd4ace..f34744f 100644
--- a/web/app/layout.tsx
+++ b/web/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata, Viewport } from "next";
import { JetBrains_Mono, Outfit } from "next/font/google";
import "./globals.css";
+import Providers from "./providers";
const outfit = Outfit({
subsets: ["latin"],
@@ -52,13 +53,24 @@ export default function RootLayout({
}: {
children: React.ReactNode;
}) {
+ const isDev = process.env.NODE_ENV !== "production";
+ const scriptSrc = isDev
+ ? "'self' 'unsafe-inline' 'unsafe-eval'"
+ : "'self' 'unsafe-inline'";
+ const connectSrc = isDev ? "'self' ws: http: https:" : "'self'";
+ const csp = [
+ "default-src 'self'",
+ `script-src ${scriptSrc}`,
+ "style-src 'self' 'unsafe-inline'",
+ "img-src 'self' data: https:",
+ "font-src 'self' https://fonts.gstatic.com",
+ `connect-src ${connectSrc}`,
+ ].join("; ");
+
return (
-
+
Skip to content
-
- {children}
+ {children}
);
}
-
-function SvgGradientDefs() {
- return (
-
- );
-}
diff --git a/web/app/page.tsx b/web/app/page.tsx
index 54d3d93..4712030 100644
--- a/web/app/page.tsx
+++ b/web/app/page.tsx
@@ -1,6 +1,9 @@
import Nav from "@/components/Nav";
import Hero from "@/components/Hero";
-import Stats from "@/components/Stats";
+import TrustStrip from "@/components/TrustStrip";
+import IntegrationsCarousel from "@/components/IntegrationsCarousel";
+import WhyQRing from "@/components/WhyQRing";
+import LiveDemo from "@/components/LiveDemo";
import Features from "@/components/Features";
import QuickStart from "@/components/QuickStart";
import McpSection from "@/components/McpSection";
@@ -8,6 +11,9 @@ import Architecture from "@/components/Architecture";
import AgentMode from "@/components/AgentMode";
import CursorPlugin from "@/components/CursorPlugin";
import Dashboard from "@/components/Dashboard";
+import Faq from "@/components/Faq";
+import FreeCallout from "@/components/FreeCallout";
+import FinalCta from "@/components/FinalCta";
import Footer from "@/components/Footer";
import WebGLBackground from "@/components/WebGLBackground";
@@ -18,7 +24,10 @@ export default function Home() {
-
+
+
+
+
@@ -26,6 +35,9 @@ export default function Home() {
+
+
+
>
diff --git a/web/app/providers.tsx b/web/app/providers.tsx
new file mode 100644
index 0000000..4593b8f
--- /dev/null
+++ b/web/app/providers.tsx
@@ -0,0 +1,20 @@
+"use client";
+
+import type { ReactNode } from "react";
+import { I18nProvider } from "react-aria-components";
+import { Toast } from "@heroui/react";
+import { CommandPaletteProvider } from "@/lib/command-palette-context";
+import CommandPalette from "@/components/CommandPalette";
+
+export default function Providers({ children }: { children: ReactNode }) {
+ return (
+
+
+ {children}
+
+
+
+
+ );
+}
+
diff --git a/web/components/AgentMode.tsx b/web/components/AgentMode.tsx
index 45dbfc4..d88296a 100644
--- a/web/components/AgentMode.tsx
+++ b/web/components/AgentMode.tsx
@@ -1,40 +1,54 @@
"use client";
-import { motion } from "motion/react";
-import FadeIn from "@/components/motion/FadeIn";
-import StaggerGroup, { itemVariants } from "@/components/motion/StaggerGroup";
-import CopyableTerminal from "@/components/CopyableTerminal";
+import { KPI, KPIGroup } from "@heroui-pro/react";
+import {
+ AlertTriangle,
+ Bot,
+ type LucideIcon,
+ RefreshCw,
+ ShieldCheck,
+} from "lucide-react";
-const agents = [
+import TerminalCard from "@/components/TerminalCard";
+
+type AgentKpi = {
+ title: string;
+ Icon: LucideIcon;
+ value: number;
+ style?: "decimal" | "percent";
+ status?: "success" | "warning" | "danger";
+ trend?: "up" | "down" | "neutral";
+ trendLabel: string;
+ desc: string;
+};
+
+const KPIS: AgentKpi[] = [
{
- icon: (
- <>
-
-
- >
- ),
title: "Continuous Monitoring",
- desc: "Scans secret health at configurable intervals. Detects expiration, anomalies, and staleness.",
+ Icon: ShieldCheck,
+ value: 0.999,
+ style: "percent",
+ status: "success",
+ trend: "up",
+ trendLabel: "uptime, last 30d",
+ desc: "Scans secret health at configurable intervals. Detects expiration, anomalies, and staleness in real time.",
},
{
- icon: (
- <>
-
-
- >
- ),
title: "Auto-Rotation",
- desc: "Automatically regenerates expired secrets using quantum noise. Zero downtime key rotation.",
+ Icon: RefreshCw,
+ value: 0,
+ status: "success",
+ trend: "neutral",
+ trendLabel: "manual rotations needed",
+ desc: "Regenerates expired secrets via providers or quantum noise. Zero downtime, zero pages.",
},
{
- icon: (
- <>
-
-
-
- >
- ),
title: "Anomaly Detection",
+ Icon: AlertTriangle,
+ value: 4,
+ status: "warning",
+ trend: "down",
+ trendLabel: "burst alerts, last 7d",
desc: "Tracks access patterns and flags burst access, unusual hours, and suspicious behavior.",
},
];
@@ -42,51 +56,93 @@ const agents = [
export default function AgentMode() {
return (
-
-
-
+
+
+
+ Background daemon
+
+
+
Agent Mode
-
-
-
- Autonomous background daemon that guards your secrets 24/7.
+
+ Autonomous background daemon that guards your secrets 24/7 — rotates,
+ audits, and alerts so you don't have to.
-
-
- {agents.map((a) => (
-
+
+
+
+ {KPIS.map((kpi, idx) => (
+
+ ))}
+
+
+
+
+ {KPIS.map((kpi) => (
+
-
-
{a.title}
- {a.desc}
-
+ {kpi.title}.{" "}
+ {kpi.desc}
+
))}
-
-
-
-
- ${" "}
- qring agent --interval 60
- --auto-rotate --verbose
+
+
+
+
+
+ ${" "}
+ qring agent{" "}
+ --interval 60 --auto-rotate --verbose
-
-
+
+
);
}
+
+function AgentStat({ kpi, isLast }: { kpi: AgentKpi; isLast: boolean }) {
+ return (
+ <>
+
+
+
+
+
+
+ {kpi.title}
+
+
+
+
+ {kpi.trend ? (
+ {kpi.trendLabel}
+ ) : null}
+
+
+ {!isLast ? : null}
+ >
+ );
+}
diff --git a/web/components/Architecture.tsx b/web/components/Architecture.tsx
index 5f4155a..14bf425 100644
--- a/web/components/Architecture.tsx
+++ b/web/components/Architecture.tsx
@@ -1,10 +1,19 @@
"use client";
-import { useState } from "react";
-import { motion, AnimatePresence } from "motion/react";
+import { motion } from "motion/react";
+import { Chip } from "@heroui/react";
+import { HoverCard } from "@heroui-pro/react";
+import { ArrowRight, Cpu, Database, Server, Terminal } from "lucide-react";
+
import FadeIn from "@/components/motion/FadeIn";
-const modules: { name: string; desc: string; section?: string }[] = [
+type Module = {
+ name: string;
+ desc: string;
+ section?: string;
+};
+
+const MODULES: Module[] = [
{ name: "Envelope", desc: "AES-256-GCM encrypted secret wrapper with metadata", section: "features" },
{ name: "Scope", desc: "Environment-aware secret resolution (global/project/team/org)", section: "features" },
{ name: "Collapse", desc: "Wavefunction collapse for automatic env detection", section: "features" },
@@ -29,124 +38,168 @@ const modules: { name: string; desc: string; section?: string }[] = [
];
export default function Architecture() {
- const [hoveredModule, setHoveredModule] = useState(null);
-
return (
-
-
-
+
+
+
+ Under the hood
+
+
Architecture
-
-
-
+
A modular core engine bridging CLI and MCP to your OS-native keyring.
+ Hover any module to see what it does.
-
-
-
- {/* Entry */}
-
-
- qring CLI
-
-
- MCP Server
-
-
+
- {/* Arrow */}
-
-
-
+
+
+
+
+
+
+
+
- {/* Core */}
-
-
- Core Engine
-
-
- {modules.map((m) => (
-
- setHoveredModule(m.name)}
- onMouseLeave={() => setHoveredModule(null)}
- onClick={() => {
- if (m.section) {
- document.getElementById(m.section)?.scrollIntoView({ behavior: "smooth" });
- }
- }}
- className="bg-accent-dim text-accent-bright font-[family-name:var(--font-mono)] text-[0.7rem] px-2.5 py-0.5 rounded border border-[rgba(14,165,233,0.2)] cursor-pointer hover:border-accent-bright hover:bg-accent/20 transition-colors"
- >
- {m.name}
-
-
- {hoveredModule === m.name && (
-
- {m.desc}
-
-
- )}
-
-
+
+
+
+ {MODULES.map((m) => (
+
))}
-
+
- {/* Arrow */}
-
-
-
+
- {/* Exit */}
-
-
- @napi-rs/keyring
-
-
- OS Keyring
-
-
+
+
+
+
);
}
+
+function Column({
+ children,
+ className = "",
+}: {
+ children: React.ReactNode;
+ className?: string;
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+function FlowArrow({ delay }: { delay: number }) {
+ return (
+
+
+
+ );
+}
+
+function SurfaceCard({
+ Icon,
+ label,
+ tone,
+ emphasized,
+ bordered,
+}: {
+ Icon: React.ElementType;
+ label: string;
+ tone: string;
+ emphasized?: boolean;
+ bordered?: "emerald";
+}) {
+ const borderClass = emphasized
+ ? "border-accent"
+ : bordered === "emerald"
+ ? "border-emerald-500"
+ : "border-border";
+ return (
+
+
+ {label}
+
+ );
+}
+
+function ModuleChip({ module }: { module: Module }) {
+ const onActivate = () => {
+ if (module.section) {
+ document
+ .getElementById(module.section)
+ ?.scrollIntoView({ behavior: "smooth" });
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {module.name}
+
+ {module.desc}
+
+
+ );
+}
diff --git a/web/components/CommandPalette.tsx b/web/components/CommandPalette.tsx
new file mode 100644
index 0000000..2e1ae4f
--- /dev/null
+++ b/web/components/CommandPalette.tsx
@@ -0,0 +1,288 @@
+"use client";
+
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { Button, Kbd, toast } from "@heroui/react";
+import { Command } from "@heroui-pro/react";
+import {
+ Copy,
+ ExternalLink,
+ FileText,
+ GitBranch,
+ Home,
+ Plug,
+ Search,
+ Terminal,
+} from "lucide-react";
+import { useRouter } from "next/navigation";
+
+import { useCommandPalette } from "@/lib/command-palette-context";
+import {
+ INSTALL_COMMANDS,
+ PACKAGE_MANAGERS,
+ type PackageManager,
+} from "@/lib/data/install";
+import { MCP_GROUPS } from "@/lib/data/mcp-tools";
+import { CLI_GROUPS } from "@/lib/data/cli-commands";
+
+type NavItem = {
+ id: string;
+ type: "nav";
+ label: string;
+ href: string;
+ description?: string;
+};
+
+type CopyItem = {
+ id: string;
+ type: "copy";
+ label: string;
+ value: string;
+ description?: string;
+};
+
+type PaletteItem = NavItem | CopyItem;
+
+function isCmdK(e: KeyboardEvent) {
+ return e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey);
+}
+
+export default function CommandPalette() {
+ const { open, setOpen, closePalette } = useCommandPalette();
+ const router = useRouter();
+ const [activePm, setActivePm] = useState("pnpm");
+
+ useEffect(() => {
+ const onKeyDown = (e: KeyboardEvent) => {
+ if (isCmdK(e)) {
+ e.preventDefault();
+ setOpen(true);
+ }
+ if (e.key === "Escape") {
+ setOpen(false);
+ }
+ };
+ window.addEventListener("keydown", onKeyDown);
+ return () => window.removeEventListener("keydown", onKeyDown);
+ }, [setOpen]);
+
+ const { pages, mcpItems, cliItems, installItem } = useMemo(() => {
+ const pages: NavItem[] = [
+ { id: "nav-home", type: "nav", label: "Home", href: "/" },
+ { id: "nav-docs", type: "nav", label: "Docs · Getting started", href: "/docs" },
+ { id: "nav-changelog", type: "nav", label: "Changelog", href: "/changelog" },
+ { id: "nav-features", type: "nav", label: "Features", href: "/#features" },
+ { id: "nav-mcp", type: "nav", label: "MCP integrations", href: "/#mcp" },
+ { id: "nav-why", type: "nav", label: "Why q-ring", href: "/#why" },
+ {
+ id: "nav-github",
+ type: "nav",
+ label: "GitHub repository",
+ href: "https://github.com/I4cTime/quantum_ring",
+ description: "github.com/I4cTime/quantum_ring",
+ },
+ {
+ id: "nav-npm",
+ type: "nav",
+ label: "npm — @i4ctime/q-ring",
+ href: "https://www.npmjs.com/package/@i4ctime/q-ring",
+ },
+ ];
+
+ const mcpItems: CopyItem[] = MCP_GROUPS.flatMap((group) =>
+ group.tools.map((tool) => ({
+ id: `mcp-${tool.name}`,
+ type: "copy",
+ label: `${group.title} · ${tool.name}`,
+ value: tool.name,
+ description: tool.desc,
+ })),
+ );
+
+ const cliItems: CopyItem[] = CLI_GROUPS.flatMap((group) =>
+ group.commands.map((cmd) => ({
+ id: `cli-${cmd.command}`,
+ type: "copy",
+ label: cmd.command,
+ value: cmd.example,
+ description: cmd.desc,
+ })),
+ );
+
+ const installItem: CopyItem = {
+ id: `copy-install-${activePm}`,
+ type: "copy",
+ label: `Copy install command (${activePm})`,
+ value: INSTALL_COMMANDS[activePm],
+ };
+
+ return { pages, mcpItems, cliItems, installItem };
+ }, [activePm]);
+
+ const itemMap = useMemo(() => {
+ const m = new Map();
+ for (const item of [...pages, ...mcpItems, ...cliItems, installItem]) {
+ m.set(item.id, item);
+ }
+ return m;
+ }, [pages, mcpItems, cliItems, installItem]);
+
+ const onSelect = useCallback(
+ async (item: PaletteItem) => {
+ if (item.type === "nav") {
+ closePalette();
+ if (item.href.startsWith("http")) {
+ window.open(item.href, "_blank", "noopener,noreferrer");
+ } else {
+ router.push(item.href);
+ }
+ return;
+ }
+
+ try {
+ await navigator.clipboard.writeText(item.value);
+ toast.success("Copied to clipboard.", { description: item.value });
+ } catch {
+ toast.danger("Copy failed.", {
+ description: "Clipboard permission denied.",
+ });
+ } finally {
+ closePalette();
+ }
+ },
+ [closePalette, router],
+ );
+
+ return (
+
+
+
+
+
+
+ Command Palette
+ q-ring
+
+
+ Esc
+
+
+
+
+
+
+
+
+
+
+
+
+ K
+
+
+
+
+ {
+ const item = itemMap.get(String(key));
+ if (item) void onSelect(item);
+ }}
+ className="max-h-[60vh] overflow-y-auto"
+ >
+
+ {pages.map((item) => (
+
+ {item.href.startsWith("http") ? (
+
+ ) : item.href.startsWith("/docs") ? (
+
+ ) : item.href.startsWith("/changelog") ? (
+
+ ) : (
+
+ )}
+
+ {item.label}
+ {item.description ? (
+ {item.description}
+ ) : null}
+
+
+ ))}
+
+
+
+
+ {PACKAGE_MANAGERS.map((pm) => (
+
+ ))}
+
+
+
+
+ {installItem.value}
+
+
+
+
+ {MCP_GROUPS.map((group) => (
+
+ {group.tools.map((tool) => (
+
+
+
+
+ {tool.name}
+
+ {tool.desc}
+
+
+ ))}
+
+ ))}
+
+ {CLI_GROUPS.map((group) => (
+
+ {group.commands.map((cmd) => (
+
+
+
+
+ {cmd.command}
+
+ {cmd.desc}
+
+
+ ))}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/web/components/CopyableTerminal.tsx b/web/components/CopyableTerminal.tsx
deleted file mode 100644
index 5b0f31e..0000000
--- a/web/components/CopyableTerminal.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-"use client";
-
-import { useState, useCallback, useRef, type ReactNode } from "react";
-
-interface CopyableTerminalProps {
- title: string;
- children: ReactNode;
- maxWidth?: string;
-}
-
-export default function CopyableTerminal({
- title,
- children,
- maxWidth = "750px",
-}: CopyableTerminalProps) {
- const [copied, setCopied] = useState(false);
- const bodyRef = useRef(null);
-
- const handleCopy = useCallback(async () => {
- if (!bodyRef.current) return;
- const text = bodyRef.current.innerText
- .replace(/^\$\s*/gm, "")
- .replace(/^#.*$/gm, "")
- .split("\n")
- .map((l) => l.trim())
- .filter(Boolean)
- .join("\n");
- try {
- await navigator.clipboard.writeText(text);
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
- } catch {
- /* clipboard may fail in insecure contexts */
- }
- }, []);
-
- return (
-
-
-
-
-
-
- {title}
-
-
-
-
- {children}
-
-
- );
-}
diff --git a/web/components/CursorPlugin.tsx b/web/components/CursorPlugin.tsx
index bb35ab2..240ce47 100644
--- a/web/components/CursorPlugin.tsx
+++ b/web/components/CursorPlugin.tsx
@@ -1,138 +1,185 @@
"use client";
-import { motion } from "motion/react";
-import FadeIn from "@/components/motion/FadeIn";
-import StaggerGroup, { itemVariants } from "@/components/motion/StaggerGroup";
-import CopyableTerminal from "@/components/CopyableTerminal";
+import { useMemo, useState } from "react";
+import { Button, Chip } from "@heroui/react";
+import {
+ ItemCard,
+ ItemCardGroup,
+ PressableFeedback,
+ Sheet,
+} from "@heroui-pro/react";
+import { ChevronRight } from "lucide-react";
-const pluginComponents = [
- {
- icon: (
- <>
-
-
-
-
-
- >
- ),
- title: "3 Rules",
- desc: "Always-on guidance: secret hygiene, q-ring workflow, and .env file safety.",
- },
- {
- icon: (
- <>
-
- >
- ),
- title: "4 Skills",
- desc: "Auto-triggered by context: management, scanning, rotation, and project onboarding.",
- },
- {
- icon: (
- <>
-
-
-
-
- >
- ),
- title: "2 Agents",
- desc: "Security auditor for proactive monitoring and secret-ops for daily management.",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "5 Commands",
- desc: "Scan secrets, health check, rotate expired, setup project, and teleport.",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "2 Hooks",
- desc: "After file edit scan and session start project context loading.",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "MCP Connector",
- desc: "Auto-connects to qring-mcp via stdio. All 44 tools available in-IDE.",
- },
-];
+import TerminalCard from "@/components/TerminalCard";
+import { PLUGIN_CATEGORIES } from "@/lib/data/plugin";
export default function CursorPlugin() {
+ const [activeId, setActiveId] = useState(null);
+
+ const active = useMemo(
+ () =>
+ activeId ? PLUGIN_CATEGORIES.find((c) => c.id === activeId) ?? null : null,
+ [activeId],
+ );
+
return (
-
-
-
+
+
+
+ Drop into Cursor
+
+
Cursor Plugin
-
-
-
+
Quantum secret management built into your IDE. Rules, skills, agents,
- commands, and hooks — all powered by q-ring's 44 MCP tools.
+ commands, hooks, and the MCP connector — pre-wired to q-ring's
+ 44 tools. Click any card for details.
-
-
- {pluginComponents.map((f) => (
-
+
+
+ {PLUGIN_CATEGORIES.map((cat) => (
+
+ key={cat.id}
+ className="relative w-full cursor-pointer overflow-hidden bg-bg-card/70 border border-border/60 hover:border-border-glow transition-colors text-left"
+ render={(props) => (
+
+
+
+
+
+
+
+ {cat.title}
+
+ {cat.count}
+
+
+
+ {cat.summary}
+
+
+
+
+
+
))}
-
-
-
-
-
+
+
+
+
+
+
# Install from the Cursor marketplace, or manually:
{"\n"}
- ${" "}
+ ${" "}
cp -r cursor-plugin/ ~/.cursor/plugins/qring/
-
-
+
+
+
+ {
+ if (!isOpen) setActiveId(null);
+ }}
+ >
+
+
+
+
+ {active ? (
+ <>
+
+
+
+
+
+
+
+ {active.title}
+
+
+ {active.count}{" "}
+ {active.count === 1 ? "asset" : "assets"} ship with the
+ plugin
+
+
+
+
+
+ {active.description.map((paragraph, idx) => (
+
+ {paragraph}
+
+ ))}
+
+
+
+ Included
+
+
+ {active.assets.map((asset) => (
+ -
+
+ {asset.name}
+
+
+ {asset.desc}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ Browse source
+
+
+
+ >
+ ) : null}
+
+
+
+
);
}
diff --git a/web/components/Dashboard.tsx b/web/components/Dashboard.tsx
index 171e37d..06fec88 100644
--- a/web/components/Dashboard.tsx
+++ b/web/components/Dashboard.tsx
@@ -1,186 +1,365 @@
"use client";
import type { ReactNode } from "react";
-import FadeIn from "@/components/motion/FadeIn";
-import CopyableTerminal from "@/components/CopyableTerminal";
+import {
+ Activity,
+ Hourglass,
+ Link2,
+ Lock,
+ type LucideIcon,
+ Shield,
+ ShieldOff,
+} from "lucide-react";
+import { Chip } from "@heroui/react";
+import {
+ ChartTooltip,
+ EmptyState,
+ LineChart,
+ RadialChart,
+} from "@heroui-pro/react";
-const dashFeatures = [
+import TerminalCard from "@/components/TerminalCard";
+
+const HEALTH_DATA = [
+ { fill: "var(--color-accent-bright)", name: "Healthy", value: 12 },
+];
+const HEALTH_TOTAL = 14;
+
+const ACCESS_DATA = [
+ { hour: "00", reads: 12 },
+ { hour: "02", reads: 8 },
+ { hour: "04", reads: 5 },
+ { hour: "06", reads: 14 },
+ { hour: "08", reads: 28 },
+ { hour: "10", reads: 41 },
+ { hour: "12", reads: 36 },
+ { hour: "14", reads: 47 },
+ { hour: "16", reads: 38 },
+ { hour: "18", reads: 22 },
+ { hour: "20", reads: 16 },
+ { hour: "22", reads: 11 },
+];
+
+const FEATURES = [
{
- icon: ,
- title: "Live SSE Updates",
- desc: "Data streams every 5 seconds via Server-Sent Events. No polling, no WebSocket complexity.",
+ Icon: Activity,
+ title: "Live SSE updates",
+ desc: "Snapshots stream every 5s. Search input, sort order, and scroll position survive each tick — no lost typing.",
},
{
- icon: (
-
- ),
- title: "Eight Panels",
- desc: "Health, environment, decay timers, superposition, entanglement, tunnels, anomalies, and audit log.",
+ Icon: Shield,
+ title: "KPI strip + 13 panels",
+ desc: "Headline KPIs, then health, env, manifest, policy, secrets table, decay, superposition, entanglement, tunnels, approvals, hooks, agent memory, anomalies, and a filterable audit feed.",
},
{
- icon: (
- <>
-
-
- >
- ),
- title: "Fully Local",
- desc: "Self-contained HTML served on localhost. No dependencies, no cloud, no data leaves your machine.",
+ Icon: Lock,
+ title: "Fully local, never values",
+ desc: "Self-contained HTML on 127.0.0.1. No dependencies, no cloud — and the page never renders secret values, only metadata.",
},
];
export default function Dashboard() {
return (
-
-
-
+
+
+
+ Operator surface
+
+
Status Dashboard
-
-
-
- Real-time visibility into every quantum subsystem. One command, zero
- config.
+
+ Headline KPIs, secrets table, manifest gaps, policy posture,
+ approvals, hooks, anomalies, and a filterable 24h audit feed —
+ streaming live from 127.0.0.1 with{" "}
+ qring status.
-
-
-
-
- {/* Browser mock */}
-
-
-
-
-
-
-
- 127.0.0.1:9876
-
-
-
-
- }>
-
-
- 12
+
+
+
+
+
+
+
+
+
+
+ 127.0.0.1:9876
+
+
+
+
+
+
+
+
+
+
+
+ } />
+
+
+
+ 12
+
+
+ of {HEALTH_TOTAL}
+
+
+
+
+
+
+ 12 healthy
+
+
+ 2 stale
+
+
+ 0
+ expired
+
-
- }>
-
+
+
+
+
+
+
prod
+
+
+ auto-detected · git/main
-
-
-
-
-
-
- >
- }
- >
-
- {[
- { w: "35%", c: "var(--color-accent)" },
- { w: "72%", c: "var(--color-warning)" },
- { w: "91%", c: "var(--color-danger)" },
- ].map((b, i) => (
-
-
+
+
+
+
+
+
+
+
+
+
+ `${Number(v)} reads`}
+ />
+ }
+ />
+
+
+
+
+
+
+ {[
+ { w: "35%", c: "var(--color-accent)", label: "API_KEY" },
+ { w: "72%", c: "var(--color-warning)", label: "DB_PASS" },
+ {
+ w: "91%",
+ c: "var(--color-danger)",
+ label: "STRIPE_KEY",
+ },
+ ].map((b) => (
+
+
+ {b.label}
+
+
+
- ))}
-
-
-
-
-
- >
- }
- >
-
- API_KEY ↔ API_BACKUP
- DB_PASS ↔ DB_REPLICA
-
-
-
-
+
+ ))}
+
+
- {/* Features */}
-
- {dashFeatures.map((f) => (
-
-
-
- {f.title}
- {f.desc}
-
+
+
+
+ API_KEY
+ ↔
+ API_BACKUP
+
+
+ DB_PASS
+ ↔
+ DB_REPLICA
+
- ))}
+
+
+
+
+
+
+
+
+
+ All clear
+
+
+ No suspicious access in the last 7 days.
+
+
+
+
-
-
-
-
-
+
+ {FEATURES.map((f) => (
+
+
+
+
+ {f.title}
+
+
+ {f.desc}
+
+
+
+ ))}
+
+
+
+
+
+
+
# Launch the dashboard (auto-opens your browser)
{"\n"}
- ${" "}
- qring status
+ ${" "}
+
+ qring status
+
{"\n\n"}
- # Custom port
+ # Custom port
{"\n"}
- ${" "}
- qring status --port 4200
+ ${" "}
+
+ qring status
+ {" "}
+ --port 4200
{"\n\n"}
-
+
# Don't auto-open the browser
{"\n"}
- ${" "}
- qring status --no-open
+ ${" "}
+
+ qring status
+ {" "}
+ --no-open
-
-
+
+
);
}
-function DashCard({ title, icon, children }: { title: string; icon: ReactNode; children: ReactNode }) {
+function DashCard({
+ title,
+ Icon,
+ children,
+ className = "",
+}: {
+ title: string;
+ Icon: LucideIcon;
+ children: ReactNode;
+ className?: string;
+}) {
return (
-
+
-
+
{title}
{children}
);
}
+
+function HealthTooltip({
+ payload,
+}: {
+ active?: boolean;
+ payload?: Array<{
+ name?: string;
+ payload?: Record;
+ value?: number | string;
+ }>;
+}) {
+ const entry = payload?.[0];
+ if (!entry?.payload) return null;
+ const name = (entry.payload["name"] as string) ?? entry.name;
+ const value = (entry.payload["value"] as number) ?? entry.value;
+ const fill = entry.payload["fill"] as string;
+ return (
+
+
+
+ {name}
+ {value}
+
+
+ );
+}
diff --git a/web/components/Faq.tsx b/web/components/Faq.tsx
new file mode 100644
index 0000000..c4dfc82
--- /dev/null
+++ b/web/components/Faq.tsx
@@ -0,0 +1,119 @@
+import { Accordion } from "@heroui/react";
+import {
+ Bot,
+ Box,
+ ChevronDown,
+ Cloud,
+ HeartHandshake,
+ KeyRound,
+ RefreshCw,
+ ScrollText,
+ ShieldCheck,
+} from "lucide-react";
+
+const FAQ_ITEMS = [
+ {
+ Icon: ShieldCheck,
+ title: "Where are my secrets actually stored?",
+ body: "Your OS keyring (macOS Keychain, Windows Credential Manager, libsecret on Linux) via @napi-rs/keyring. q-ring never touches plaintext disk and never round-trips your secrets to a server.",
+ },
+ {
+ Icon: Cloud,
+ title: "Is there a SaaS or cloud component?",
+ body: "No. q-ring is 100% local. The optional dashboard runs on 127.0.0.1, the MCP server runs on stdio, and the CLI never makes outbound calls except when you explicitly run validate/rotate/JIT operations against your own providers.",
+ },
+ {
+ Icon: Bot,
+ title: "How does it integrate with AI editors?",
+ body: "Two ways. The Cursor plugin auto-loads rules, skills, agents, slash commands, and hooks. Every other editor (Kiro, Claude Code, VS Code, Windsurf) gets the same 44 tools by adding a single MCP server config — same binary, no extra wiring.",
+ },
+ {
+ Icon: KeyRound,
+ title: "Can multiple environments share one key name?",
+ body: "Yes — that's the whole point of Superposition. One key like API_KEY can carry distinct values for dev, staging, and prod. Wavefunction Collapse picks the right one based on flags, env vars, git branch, and your project manifest.",
+ },
+ {
+ Icon: RefreshCw,
+ title: "What happens when a secret expires?",
+ body: "Decay-aware reads will refuse expired secrets and warn on stale ones. Pair with the Agent for autonomous rotation, or run `qring rotate` to issue a provider-native rotation (with a quantum-noise fallback when the provider has no rotation API).",
+ },
+ {
+ Icon: Box,
+ title: "How do I migrate from .env files?",
+ body: "Run `qring import .env --skip-existing`. Comments, quoting, and escape sequences are preserved. Then delete the .env file and use `qring exec`, `qring env:generate`, or the MCP `get_secret` tool from your code.",
+ },
+ {
+ Icon: Bot,
+ title: "Is this safe for AI agents to operate?",
+ body: "Yes. Every tool call is gated by your governance policy, sensitive secrets require HMAC approvals, and every read/write/delete is logged in a tamper-evident audit chain. Agents never see plaintext unless your policy allows it.",
+ },
+ {
+ Icon: HeartHandshake,
+ title: "What's the license? Is there a paid tier?",
+ body: "AGPL-3.0, free forever. There is no paid tier, no telemetry, and no upsell. If you want to support the work, the Ko-fi link in the header is appreciated — but it never unlocks anything.",
+ },
+];
+
+export default function Faq() {
+ return (
+
+
+
+
+ Common questions
+
+
+ Frequently asked
+
+
+ Everything you might want to know before adopting q-ring. If something
+ isn't covered here, open an issue on GitHub — we update this list
+ regularly.
+
+
+
+
+ {FAQ_ITEMS.map((item) => (
+
+
+
+
+
+
+
+ {item.title}
+
+
+
+
+
+
+
+
+ {item.body}
+
+
+
+ ))}
+
+
+
+
+ Still curious? Open a question and we'll add it.
+
+
+
+ );
+}
diff --git a/web/components/Features.tsx b/web/components/Features.tsx
index 0a21954..123b3a9 100644
--- a/web/components/Features.tsx
+++ b/web/components/Features.tsx
@@ -1,319 +1,254 @@
"use client";
-import { motion } from "motion/react";
-import FadeIn from "@/components/motion/FadeIn";
-import StaggerGroup, { itemVariants } from "@/components/motion/StaggerGroup";
+import { useMemo, useState, type Key } from "react";
+import { Button, Chip, Tabs, toast } from "@heroui/react";
+import {
+ ItemCard,
+ ItemCardGroup,
+ PressableFeedback,
+ Sheet,
+} from "@heroui-pro/react";
+import { ChevronRight, Copy } from "lucide-react";
-const features = [
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Superposition",
- desc: "One key holds values for dev, staging, and prod simultaneously. The correct value resolves based on your current context.",
- cmd: 'qring set API_KEY "sk-dev" --env dev',
- },
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Wavefunction Collapse",
- desc: "Auto-detects your environment from flags, env vars, git branches, and project config. Zero manual switching.",
- cmd: "qring env",
- },
- {
- icon: (
- <>
-
-
-
-
- >
- ),
- title: "Quantum Decay",
- desc: "Secrets with TTL. Expired secrets are blocked, stale ones trigger warnings. Set explicit expiry or time-to-live.",
- cmd: 'qring set TOKEN "tok-..." --ttl 3600',
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Observer Effect",
- desc: "Every read, write, and delete is logged. Access patterns are tracked for anomaly detection and audit trails.",
- cmd: "qring audit --anomalies",
- },
- {
- icon: ,
- title: "Quantum Noise",
- desc: "Generate cryptographically strong secrets in API key, password, UUID, token, or hex formats.",
- cmd: "qring generate --format api-key",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Entanglement",
- desc: "Link secrets across projects. When you rotate one, all entangled copies update automatically.",
- cmd: "qring entangle API_KEY API_KEY_BACKUP",
- },
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Tunneling",
- desc: "Ephemeral secrets that exist only in memory. Never touch disk. Optional TTL and max-read self-destruction.",
- cmd: 'qring tunnel create "temp-tok" --max-reads 1',
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Teleportation",
- desc: "Pack secrets into AES-256-GCM encrypted bundles for secure transfer between machines.",
- cmd: 'qring teleport pack --keys "API_KEY"',
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Liveness Validation",
- desc: "Test if a secret is actually valid with its target service. Auto-detects OpenAI, Stripe, GitHub, and AWS from key prefixes.",
- cmd: "qring validate --all",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Hooks",
- desc: 'Register shell commands, HTTP webhooks, or process signals that fire when secrets are written, deleted, or rotated.',
- cmd: 'qring hook add --key DB_PASS --exec "..."',
- },
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Project Manifest",
- desc: "Declare required secrets in .q-ring.json and validate project readiness with a single command.",
- cmd: "qring check",
- },
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Import",
- desc: "Migrate from .env files in one command. Supports standard dotenv syntax with comments, quotes, and escape sequences.",
- cmd: "qring import .env --skip-existing",
- },
- {
- icon: (
- <>
-
-
-
-
- >
- ),
- title: "Env Sync",
- desc: "Generate .env files from the project manifest, resolving each key with environment-aware superposition collapse.",
- cmd: "qring env:generate --output .env",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Secure Execution",
- desc: "Run commands with secrets injected into the environment. All known values are auto-redacted from stdout and stderr.",
- cmd: "qring exec -- npm run deploy",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Codebase Scanner",
- desc: "Detect hardcoded credentials using regex heuristics and Shannon entropy analysis. Migrate legacy codebases fast.",
- cmd: "qring scan .",
- },
- {
- icon: (
- <>
-
-
-
- >
- ),
- title: "Governance Policy",
- desc: "Define MCP tool gating, key access restrictions, exec allowlists, and secret lifecycle rules in .q-ring.json.",
- cmd: "qring policy",
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "User Approvals",
- desc: "Zero-trust MCP access. Sensitive secrets require HMAC-verified, scoped, time-limited approval tokens before reads.",
- cmd: 'qring approve PROD_DB_URL --for 3600',
- },
- {
- icon: (
- <>
-
- >
- ),
- title: "JIT Provisioning",
- desc: "Dynamically generate short-lived tokens on read. AWS STS role assumption, generic HTTP endpoints, and caching.",
- cmd: 'qring set AWS_KEYS \'...\' --jit-provider aws-sts',
- },
- {
- icon: (
- <>
-
- >
- ),
- title: "Agent Memory",
- desc: "Encrypted, persistent key-value store that survives across AI agent sessions. Remember decisions and context.",
- cmd: 'qring remember last_rotation "Rotated on 2026-03-21"',
- },
- {
- icon: (
- <>
-
-
-
-
-
- >
- ),
- title: "Secret Linter",
- desc: "Scan specific files for hardcoded secrets. Use --fix to auto-replace with process.env references and store in q-ring.",
- cmd: "qring lint src/config.ts --fix",
- },
- {
- icon: (
- <>
-
-
-
-
- >
- ),
- title: "Team & Org Scopes",
- desc: "Share secrets across teams and orgs. Resolution cascades: project → team → org → global (most specific wins).",
- cmd: 'qring set SHARED_KEY "sk-..." --team my-team',
- },
- {
- icon: (
- <>
-
-
- >
- ),
- title: "Issuer-Native Rotation",
- desc: "Attempt provider-native secret rotation for supported services, or fall back to local quantum noise generation.",
- cmd: "qring rotate STRIPE_KEY",
- },
- {
- icon: (
- <>
-
- >
- ),
- title: "Secret Analytics",
- desc: "Analyze usage patterns: most accessed secrets, unused/stale keys, scope optimization, and rotation recommendations.",
- cmd: "qring analyze",
- },
+import {
+ CATEGORY_LABELS,
+ FEATURES,
+ type Feature,
+ type FeatureCategory,
+} from "@/lib/data/features";
+
+type Filter = "all" | FeatureCategory;
+
+const FILTERS: { id: Filter; label: string }[] = [
+ { id: "all", label: "All" },
+ { id: "quantum", label: CATEGORY_LABELS.quantum },
+ { id: "storage", label: CATEGORY_LABELS.storage },
+ { id: "security", label: CATEGORY_LABELS.security },
+ { id: "agent", label: CATEGORY_LABELS.agent },
];
+const CATEGORY_COLOR: Record<
+ FeatureCategory,
+ "accent" | "default" | "warning" | "success"
+> = {
+ quantum: "accent",
+ storage: "default",
+ security: "warning",
+ agent: "success",
+};
+
export default function Features() {
+ const [filter, setFilter] = useState("all");
+ const [activeId, setActiveId] = useState(null);
+
+ const visible = useMemo(
+ () => (filter === "all" ? FEATURES : FEATURES.filter((f) => f.category === filter)),
+ [filter],
+ );
+
+ const active = useMemo(
+ () => (activeId ? FEATURES.find((f) => f.id === activeId) ?? null : null),
+ [activeId],
+ );
+
+ const onCopy = async (text: string) => {
+ try {
+ await navigator.clipboard.writeText(text);
+ toast.success("Copied to clipboard.", { description: text });
+ } catch {
+ toast.danger("Copy failed.", {
+ description: "Clipboard permission denied.",
+ });
+ }
+ };
+
return (
-
-
-
+
+
+
+ What ships in q-ring
+
+
Quantum Features
-
-
-
- Twenty-four capabilities inspired by quantum physics, engineered for
- real-world secret management.
+
+ Twenty-three capabilities inspired by quantum physics — engineered for
+ real-world secret management. Click any feature to peek at how it works.
-
-
- {features.map((f) => (
-
-
-
- {f.title}
-
-
- {f.desc}
-
-
- {f.cmd}
-
-
+
+
+
+ setFilter(key as Filter)}
+ variant="secondary"
+ >
+
+
+ {FILTERS.map((f) => (
+
+ {f.label}
+
+
+ ))}
+
+
+
+
+
+
+ {visible.map((feature) => (
+ setActiveId(feature.id)}
+ />
))}
-
+
+
+ {
+ if (!isOpen) setActiveId(null);
+ }}
+ >
+
+
+
+
+ {active ? (
+ <>
+
+
+
+
+
+
+
+ {active.title}
+
+
+ {CATEGORY_LABELS[active.category]}
+
+
+
+
+
+
+ {active.desc}
+
+ {active.long.map((paragraph, idx) => (
+
+ {paragraph}
+
+ ))}
+
+
+
+ Try it
+
+
+
+ {active.cmd}
+
+
+
+
+
+ {active.related && active.related.length > 0 ? (
+
+
+ Pairs well with
+
+
+ {active.related.map((label) => (
+
+ {label}
+
+ ))}
+
+
+ ) : null}
+
+
+
+
+
+
+ Read the docs
+
+
+
+ >
+ ) : null}
+
+
+
+
);
}
+
+function FeatureCard({
+ feature,
+ onOpen,
+}: {
+ feature: Feature;
+ onOpen: () => void;
+}) {
+ return (
+
+ className="relative w-full cursor-pointer overflow-hidden bg-bg-card/70 border border-border/60 hover:border-border-glow transition-colors text-left"
+ render={(props) => }
+ >
+
+
+
+
+
+
+ {feature.title}
+
+
+ {feature.desc}
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/FinalCta.tsx b/web/components/FinalCta.tsx
new file mode 100644
index 0000000..76b4566
--- /dev/null
+++ b/web/components/FinalCta.tsx
@@ -0,0 +1,64 @@
+import NextLink from "next/link";
+import { buttonVariants } from "@heroui/styles";
+import { ArrowRight, BookOpen, Sparkles } from "lucide-react";
+
+import { BrandGithub } from "@/components/icons/BrandIcons";
+
+export default function FinalCta() {
+ return (
+
+
+
+
+
+
+ Ready when you are
+
+
+ Stop leaking secrets.
+
+ Start shipping with confidence.
+
+
+ Three minutes to install, zero seconds of cloud setup. q-ring meets
+ your AI agents where they already live — and your secrets never have
+ to.
+
+
+
+
+
+ Free under AGPL-3.0. No accounts. No telemetry. No vendor lock-in.
+
+
+
+
+ );
+}
diff --git a/web/components/Footer.tsx b/web/components/Footer.tsx
index d7a6c0b..a324212 100644
--- a/web/components/Footer.tsx
+++ b/web/components/Footer.tsx
@@ -1,45 +1,144 @@
-import Link from "next/link";
+import Image from "next/image";
+import NextLink from "next/link";
+import { Link } from "@heroui/react";
+import { ExternalLink, Heart, Package, ScrollText } from "lucide-react";
+
+import { BrandGithub } from "@/components/icons/BrandIcons";
+import { PROJECT_VERSION } from "@/lib/data/version";
+
+const PRODUCT_LINKS = [
+ { href: "/docs", label: "Docs", external: false },
+ { href: "/changelog", label: "Changelog", external: false },
+ { href: "/#features", label: "Features", external: false },
+ { href: "/#mcp", label: "MCP integrations", external: false },
+] as const;
+
+const COMMUNITY_LINKS = [
+ {
+ href: "https://github.com/I4cTime/quantum_ring",
+ label: "GitHub",
+ Icon: BrandGithub,
+ },
+ {
+ href: "https://www.npmjs.com/package/@i4ctime/q-ring",
+ label: "npm",
+ Icon: Package,
+ },
+ {
+ href: "https://ko-fi.com/i4ctime",
+ label: "Ko-fi",
+ Icon: Heart,
+ },
+ {
+ href: "https://github.com/I4cTime/quantum_ring/blob/main/LICENSE",
+ label: "AGPL-3.0",
+ Icon: ScrollText,
+ },
+] as const;
export default function Footer() {
return (
-