import * as React from "react"; import { ArrowDown, ArrowRight, Cloud, KeyRound, Lock, LockOpen, Monitor, ScrollText, ShieldCheck } from "lucide-react"; import { cn } from "@/lib/utils"; import { Reveal } from "./reveal"; /* ------------------------------------------------------------------------------------------------ * Browser → PolyLLM encrypted server layer → Provider * ---------------------------------------------------------------------------------------------- */ function Node({ icon, title, sub, items, tone = "default", className }: { icon: React.ReactNode; title: string; sub: string; items: string[]; tone?: "default" | "accent"; className?: string }) { return (
{icon}

{title}

{sub}

); } function Arrow({ label }: { label: string }) { return (
{label}
); } export function SecurityFlow() { return ( } title="Your browser" sub="never holds a provider key" items={["Sends the key once, over TLS, when you connect a provider", "Receives only a hint like sk-••••••••9A2K afterwards", "Session in an HttpOnly cookie — not readable by scripts"]} /> } title="PolyLLM server" sub="encrypted key layer" tone="accent" items={["Encrypts the key with AES-256-GCM (HKDF-derived data key, per-user AAD) before it is stored", "Decrypts in memory only when it is about to call the provider", "Never logs, returns or serializes the key; logs pass through a secret redactor"]} /> } title="Provider API" sub="the one you selected" items={["Receives your prompt and your key directly from PolyLLM's server", "Bills your own account at list price — no markup, no reseller", "Nothing is routed through third-party inference brokers"]} /> ); } /* ------------------------------------------------------------------------------------------------ * API key lifecycle * ---------------------------------------------------------------------------------------------- */ const LIFECYCLE: { icon: React.ReactNode; title: string; body: React.ReactNode; code?: string }[] = [ { icon: , title: "1 · You paste a key", body: "Sent once over TLS to PUT /api/providers. Checked for shape (8–512 characters, no whitespace) and validated against the provider with a real request, so you know it works before it is saved.", }, { icon: , title: "2 · Encrypted with AES-256-GCM", body: ( <> The data key is derived from API_KEY_ENCRYPTION_SECRET with HKDF-SHA256 (info polyllm:provider-key:v1), so the raw secret never touches the cipher. A random 12-byte IV and a 16-byte authentication tag protect every envelope. The additional authenticated data is userId|provider: a ciphertext copied to another user or provider cannot be decrypted. ), code: "v1... (base64url)", }, { icon: , title: "3 · Stored with a hint and a fingerprint", body: ( <> The database row holds the envelope, a display hint (recognizable prefix + last 4 characters) and a SHA-256 fingerprint used only to detect “same key” on rotation. PublicConnection, the shape the browser sees, has no key field at all. ), }, { icon: , title: "4 · Decrypted only for the request", body: ( <> getDecryptedKey() runs immediately before a provider call — chat, Arena, key validation, model sync — and the plaintext lives in memory only for that call. It is never written to logs, error messages or exports. ), }, { icon: , title: "5 · Audited, rotatable, deletable", body: ( <> Adding, replacing and removing a key writes an audit event (provider.key_added, provider.key_replaced, provider.key_deleted) with the requesting IP. Removing a key deletes the encrypted row immediately; revoking it in the provider console makes the stored copy useless. ), }, ]; export function KeyLifecycle() { return (
    {LIFECYCLE.map((step, i) => (
    {step.icon}

    {step.title}

    {step.body}

    {step.code ?
    {step.code}
    : null}
    ))}
); }