vault1984-web/index.html

639 lines
44 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vault1984 — AI-native password manager with field-level encryption</title>
<meta name="description" content="Field-level two-tier encryption for password managers that live alongside AI assistants. Your AI gets what it needs. Your secrets stay yours.">
<link rel="stylesheet" href="/tailwind.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
body { background-color: #0A1628; }
.gradient-text {
background: linear-gradient(135deg, #22C55E 0%, #4ade80 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.card-hover {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-hover:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
html { scroll-behavior: smooth; }
@keyframes pulse-ring {
0% { transform: scale(0.8); opacity: 1; }
100% { transform: scale(2.5); opacity: 0; }
}
@keyframes pulse-dot {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.15); }
}
.pulse-dot { animation: pulse-dot 2s ease-in-out infinite; }
.pulse-ring { animation: pulse-ring 2s ease-out infinite; }
.pulse-ring-2 { animation: pulse-ring 2s ease-out infinite 0.5s; }
</style>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
</head>
<body class="font-sans text-gray-300 antialiased">
<!-- Nav -->
<nav class="fixed top-0 w-full z-50 bg-navy/80 backdrop-blur-md border-b border-white/5">
<div class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<a href="/" class="font-mono font-bold text-3xl text-white tracking-tight leading-none">vault<span class="text-accent font-mono">1984</span></a>
<div class="hidden md:flex items-center gap-6 text-sm">
<a href="https://github.com/johanjongsma/vault1984" target="_blank" rel="noopener" class="text-gray-400 hover:text-white transition-colors">GitHub</a>
<a href="/hosted" class="font-semibold transition-colors flex items-center gap-1.5" style="color:#D4AF37"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:#D4AF37;animation:hostedPulse 2s ease-in-out infinite"></span>Hosted</a>
<a href="/install.html" class="text-gray-400 hover:text-white transition-colors">Self-host</a>
<a href="#" class="border border-gray-600 text-gray-300 hover:border-gray-400 hover:text-white px-4 py-1.5 rounded-lg transition-colors text-sm">Sign in</a>
<a href="/pricing.html" class="bg-accent hover:bg-accent-hover text-black font-medium px-4 py-1.5 rounded-lg transition-colors text-sm">Get hosted &mdash; $12/yr</a>
</div>
<button id="mobile-menu-btn" class="md:hidden text-gray-400">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
</div>
<div id="mobile-menu" class="hidden md:hidden border-t border-white/5 bg-navy/95 backdrop-blur-md">
<div class="px-6 py-4 space-y-3">
<a href="https://github.com/johanjongsma/vault1984" target="_blank" rel="noopener" class="block text-gray-400 hover:text-white">GitHub</a>
<a href="/hosted" class="block font-semibold" style="color:#D4AF37">✦ Hosted</a>
<a href="/install.html" class="block text-gray-400 hover:text-white">Self-host</a>
<a href="#" class="block text-gray-400 hover:text-white">Sign in</a>
<a href="/pricing.html" class="block bg-accent hover:bg-accent-hover text-black font-medium px-4 py-2 rounded-lg text-center">Get hosted &mdash; $12/yr</a>
</div>
</div>
</nav>
<!-- Hero -->
<section class="pt-32 pb-20 px-6">
<div class="max-w-7xl mx-auto">
<div class="grid lg:grid-cols-2 gap-16 items-center">
<div>
<p class="text-sm font-mono text-green-400 mb-6 tracking-widest uppercase opacity-75">George Orwell — 1984</p>
<h1 class="text-4xl md:text-5xl lg:text-[3.25rem] font-bold text-white leading-tight tracking-tight font-mono">
"If you want to keep a secret, you must also hide it from yourself."
</h1>
<p class="mt-6 text-lg text-gray-300 leading-relaxed max-w-xl">
We did. Your L2 key is derived in your browser from your Touch ID. Our servers have never seen it. They could not decrypt your private fields even if they wanted to. Or anybody else.
</p>
<div class="mt-8 flex flex-wrap items-center gap-4">
<a href="/pricing.html" class="bg-accent hover:bg-accent-hover text-black font-semibold px-6 py-3 rounded-lg transition-colors">
Get hosted &mdash; $12/yr
</a>
<a href="/install.html" class="text-accent hover:text-white font-medium transition-colors">
Self-host free &rarr;
</a>
</div>
</div>
<!-- Hero SVG: L1/L2 split diagram -->
<div class="flex justify-center">
<svg viewBox="0 0 480 380" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-full max-w-md">
<!-- Background -->
<rect x="0" y="0" width="480" height="380" rx="12" fill="#111f38"/>
<!-- Top labels -->
<text x="130" y="35" font-family="JetBrains Mono, monospace" font-size="11" fill="#94a3b8" text-anchor="middle">AI Agent</text>
<text x="350" y="35" font-family="JetBrains Mono, monospace" font-size="11" fill="#94a3b8" text-anchor="middle">You only</text>
<!-- Arrows -->
<path d="M130 42 L130 58" stroke="#22C55E" stroke-width="1.5" marker-end="url(#arrowGreen)"/>
<path d="M350 42 L350 58" stroke="#EF4444" stroke-width="1.5" marker-end="url(#arrowRed)"/>
<defs>
<marker id="arrowGreen" markerWidth="8" markerHeight="6" refX="4" refY="3" orient="auto"><path d="M0,0 L4,3 L0,6" fill="none" stroke="#22C55E" stroke-width="1.5"/></marker>
<marker id="arrowRed" markerWidth="8" markerHeight="6" refX="4" refY="3" orient="auto"><path d="M0,0 L4,3 L0,6" fill="none" stroke="#EF4444" stroke-width="1.5"/></marker>
</defs>
<!-- L1 Column -->
<rect x="30" y="65" width="200" height="260" rx="8" fill="none" stroke="#22C55E" stroke-width="1" stroke-opacity="0.3"/>
<rect x="30" y="65" width="200" height="30" rx="8" fill="#22C55E" fill-opacity="0.1"/>
<text x="130" y="85" font-family="JetBrains Mono, monospace" font-size="12" fill="#22C55E" text-anchor="middle" font-weight="600">L1 — AI can read</text>
<!-- L1 items -->
<g>
<rect x="50" y="115" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="80" y="138" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">github_token</text>
<circle cx="192" cy="133" r="8" fill="#22C55E" fill-opacity="0.15"/>
<path d="M188 133 L190.5 135.5 L196 130" stroke="#22C55E" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="50" y="163" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="80" y="186" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">ssh_key</text>
<circle cx="192" cy="181" r="8" fill="#22C55E" fill-opacity="0.15"/>
<path d="M188 181 L190.5 183.5 L196 178" stroke="#22C55E" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="50" y="211" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="80" y="234" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">totp_github</text>
<circle cx="192" cy="229" r="8" fill="#22C55E" fill-opacity="0.15"/>
<path d="M188 229 L190.5 231.5 L196 226" stroke="#22C55E" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="50" y="259" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="80" y="282" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">oauth_slack</text>
<circle cx="192" cy="277" r="8" fill="#22C55E" fill-opacity="0.15"/>
<path d="M188 277 L190.5 279.5 L196 274" stroke="#22C55E" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<!-- L2 Column -->
<rect x="250" y="65" width="200" height="260" rx="8" fill="none" stroke="#EF4444" stroke-width="1" stroke-opacity="0.3"/>
<rect x="250" y="65" width="200" height="30" rx="8" fill="#EF4444" fill-opacity="0.1"/>
<text x="350" y="85" font-family="JetBrains Mono, monospace" font-size="12" fill="#EF4444" text-anchor="middle" font-weight="600">L2 — you only</text>
<!-- L2 items -->
<g>
<rect x="270" y="115" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="300" y="138" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">credit_card</text>
<rect x="408" y="125" width="16" height="16" rx="3" fill="#EF4444" fill-opacity="0.15"/>
<path d="M413 131 L413 135 M416 131 L416 135 M411 133 L411 129 Q411 127 413 127 L416 127 Q418 127 418 129 L418 133 Z" stroke="#EF4444" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="270" y="163" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="300" y="186" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">cvv</text>
<rect x="408" y="173" width="16" height="16" rx="3" fill="#EF4444" fill-opacity="0.15"/>
<path d="M413 179 L413 183 M416 179 L416 183 M411 181 L411 177 Q411 175 413 175 L416 175 Q418 175 418 177 L418 181 Z" stroke="#EF4444" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="270" y="211" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="300" y="234" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">passport</text>
<rect x="408" y="221" width="16" height="16" rx="3" fill="#EF4444" fill-opacity="0.15"/>
<path d="M413 227 L413 231 M416 227 L416 231 M411 229 L411 225 Q411 223 413 223 L416 223 Q418 223 418 225 L418 229 Z" stroke="#EF4444" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g>
<rect x="270" y="259" width="160" height="36" rx="6" fill="#0A1628"/>
<text x="300" y="282" font-family="JetBrains Mono, monospace" font-size="11" fill="#d1d5db">ssn</text>
<rect x="408" y="269" width="16" height="16" rx="3" fill="#EF4444" fill-opacity="0.15"/>
<path d="M413 275 L413 279 M416 275 L416 279 M411 277 L411 273 Q411 271 413 271 L416 271 Q418 271 418 273 L418 277 Z" stroke="#EF4444" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<!-- Center vault icon -->
<rect x="224" y="340" width="32" height="28" rx="4" fill="#111f38" stroke="#94a3b8" stroke-width="1"/>
<circle cx="240" cy="352" r="3" fill="none" stroke="#94a3b8" stroke-width="1"/>
<line x1="240" y1="355" x2="240" y2="360" stroke="#94a3b8" stroke-width="1"/>
</svg>
</div>
</div>
</div>
</div>
</section>
<!-- The Problem -->
<section class="py-20 px-6 border-t border-white/5">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold text-white text-center mb-4">The problem</h2>
<p class="text-gray-400 text-center mb-14 max-w-2xl mx-auto">Every password manager was built before AI agents existed. Now they need to catch up.</p>
<div class="grid md:grid-cols-3 gap-6">
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-danger/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-danger" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">All-or-nothing is broken</h3>
<p class="text-gray-400 text-sm leading-relaxed">All others give your AI agent access to everything in your vault, or nothing at all. There's no middle ground. Your AI needs your GitHub token — it shouldn't also see your passport number.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-danger/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-danger" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">Policy isn't security</h3>
<p class="text-gray-400 text-sm leading-relaxed">"AI-safe" vaults still decrypt everything server-side. They rely on access policies that can be overridden, misconfigured, or bypassed. If the server can read it, it's not truly private.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-danger/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-danger" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">Agents need credentials — and 2FA codes</h3>
<p class="text-gray-400 text-sm leading-relaxed">Your AI can't log in to a service, get past two-factor authentication, or rotate API keys without credential access. vault<span class="text-accent font-mono">1984</span> lets it do all three — without exposing your credit card or passport to the same pipeline.</p>
</div>
</div>
</div>
<!-- How it works — EA analogy -->
<section class="py-20 px-6 border-t border-white/5">
<div class="max-w-7xl mx-auto">
<div class="max-w-3xl mx-auto text-center mb-14">
<h2 class="text-3xl md:text-4xl font-bold text-white mb-8">How it works</h2>
<blockquote class="text-2xl md:text-3xl font-semibold text-white leading-snug">
"Your assistant can book your flights.<br>
<span class="gradient-text">Not read your diary.</span>"
</blockquote>
<p class="mt-6 text-gray-400">Your passwords are stored on the vault server — yours to self-host, or ours to run. Every field is encrypted. But some fields get a second lock. That second key is derived from your fingerprint and only exists in your browser. The server holds the safe. Only you hold that key.</p>
</div>
<div class="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
<!-- L1 -->
<div class="rounded-xl border border-accent/30 bg-accent/5 p-8">
<div class="flex items-center gap-3 mb-6">
<span class="text-xs font-mono font-semibold text-accent bg-accent/10 px-2.5 py-1 rounded">L1</span>
<span class="text-sm font-semibold text-accent">AI-readable</span>
</div>
<p class="text-gray-400 text-sm mb-5">Encrypted at rest, decryptable by the vault server. Your AI agent reads these via MCP.</p>
<ul class="space-y-3">
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-accent flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
API keys &amp; tokens
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-accent flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
SSH keys
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-accent flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
TOTP 2FA codes — AI generates them for you (no more copy-paste from your phone)
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-accent flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
OAuth tokens
</li>
</ul>
</div>
<!-- L2 -->
<div class="rounded-xl border border-danger/30 bg-danger/5 p-8">
<div class="flex items-center gap-3 mb-6">
<span class="text-xs font-mono font-semibold text-danger bg-danger/10 px-2.5 py-1 rounded">L2</span>
<span class="text-sm font-semibold text-danger">Touch ID only</span>
</div>
<p class="text-gray-400 text-sm mb-5">Encrypted client-side with WebAuthn PRF. The server never sees the plaintext. Ever.</p>
<ul class="space-y-3">
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-danger flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
Credit card numbers
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-danger flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
CVV
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-danger flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
Passport &amp; SSN
</li>
<li class="flex items-center gap-3 text-sm text-gray-300">
<svg class="w-4 h-4 text-danger flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
Private signing keys
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Key Features -->
<section class="py-20 px-6 border-t border-white/5">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold text-white text-center mb-4">Built different</h2>
<p class="text-gray-400 text-center mb-14 max-w-2xl mx-auto">Not another password manager with an AI checkbox. The architecture is the feature.</p>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">Field-level AI visibility</h3>
<p class="text-gray-400 text-sm leading-relaxed">Each field in an entry has its own encryption tier. Your AI reads the username, not the CVV. Same entry, different access.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">WebAuthn PRF</h3>
<p class="text-gray-400 text-sm leading-relaxed">L2 encryption uses WebAuthn PRF — a cryptographic key derived from your biometric hardware. Math, not policy. The server literally cannot decrypt it.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">AI-powered 2FA</h3>
<p class="text-gray-400 text-sm leading-relaxed">Store TOTP secrets as L1 fields. Your AI agent generates time-based codes on demand via MCP — no more switching to your authenticator app.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">Scoped MCP tokens</h3>
<p class="text-gray-400 text-sm leading-relaxed">Create separate MCP tokens per agent or integration. Each token sees only its designated entries. Compromise one, the rest stay clean.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">One binary, one file</h3>
<p class="text-gray-400 text-sm leading-relaxed">No Docker. No Postgres. No Redis. One Go binary, one SQLite file. Runs on a Raspberry Pi. Runs on a VPS. Runs on your laptop.</p>
</div>
<div class="bg-navy-light rounded-xl p-8 card-hover">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-5">
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/></svg>
</div>
<h3 class="text-lg font-semibold text-white mb-3">LLM field mapping</h3>
<p class="text-gray-400 text-sm leading-relaxed">Import from any password manager and the built-in LLM automatically classifies which fields should be L1 (AI-visible) vs L2 (private).</p>
</div>
</div>
</div>
</div>
</section>
<!-- Multi-agent swarms -->
<section class="py-20 px-6 bg-navy-light/50 border-t border-b border-white/5">
<div class="max-w-7xl mx-auto">
<div class="grid lg:grid-cols-2 gap-16 items-center">
<div>
<h2 class="text-3xl md:text-4xl font-bold text-white mb-6">
10 agents.<br>
<span class="gradient-text">Each gets exactly what it needs.</span>
</h2>
<p class="text-gray-400 leading-relaxed mb-6">Create scoped MCP tokens per agent. One compromised agent exposes one agent's scope — not your entire vault.</p>
<div class="bg-navy rounded-xl p-5 font-mono text-sm overflow-x-auto">
<div class="text-gray-500 mb-1">~/.claude/mcp.json</div>
<pre class="text-gray-300 leading-relaxed"><code>{
"mcpServers": {
"vault-dev": {
"url": "http://localhost:1984/mcp",
"headers": {
"Authorization": "Bearer <span class="text-accent">mcp_dev_a3f8...</span>"
}
},
"vault-social": {
"url": "http://localhost:1984/mcp",
"headers": {
"Authorization": "Bearer <span class="text-accent">mcp_social_7b2e...</span>"
}
}
}
}</code></pre>
</div>
</div>
<!-- Multi-agent SVG -->
<div class="flex justify-center">
<svg viewBox="0 0 400 360" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-full max-w-sm">
<!-- Center vault -->
<rect x="160" y="140" width="80" height="80" rx="12" fill="#111f38" stroke="#94a3b8" stroke-width="1.5"/>
<text x="200" y="175" font-family="JetBrains Mono, monospace" font-size="10" fill="#94a3b8" text-anchor="middle">vault</text>
<text x="200" y="195" font-family="JetBrains Mono, monospace" font-size="14" fill="white" text-anchor="middle" font-weight="600">1984</text>
<!-- Agent 1 — dev -->
<circle cx="80" cy="60" r="32" fill="#22C55E" fill-opacity="0.08" stroke="#22C55E" stroke-width="1"/>
<text x="80" y="56" font-family="JetBrains Mono, monospace" font-size="9" fill="#22C55E" text-anchor="middle">Agent 1</text>
<text x="80" y="68" font-family="JetBrains Mono, monospace" font-size="8" fill="#94a3b8" text-anchor="middle">dev</text>
<line x1="108" y1="80" x2="165" y2="145" stroke="#22C55E" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
<!-- Agent 2 — social -->
<circle cx="320" cy="60" r="32" fill="#22C55E" fill-opacity="0.08" stroke="#22C55E" stroke-width="1"/>
<text x="320" y="56" font-family="JetBrains Mono, monospace" font-size="9" fill="#22C55E" text-anchor="middle">Agent 2</text>
<text x="320" y="68" font-family="JetBrains Mono, monospace" font-size="8" fill="#94a3b8" text-anchor="middle">social</text>
<line x1="292" y1="80" x2="235" y2="145" stroke="#22C55E" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
<!-- Agent 3 — finance -->
<circle cx="50" cy="220" r="32" fill="#22C55E" fill-opacity="0.08" stroke="#22C55E" stroke-width="1"/>
<text x="50" y="216" font-family="JetBrains Mono, monospace" font-size="9" fill="#22C55E" text-anchor="middle">Agent 3</text>
<text x="50" y="228" font-family="JetBrains Mono, monospace" font-size="8" fill="#94a3b8" text-anchor="middle">finance</text>
<line x1="78" y1="204" x2="164" y2="190" stroke="#22C55E" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
<!-- Agent 4 — infra -->
<circle cx="350" cy="220" r="32" fill="#22C55E" fill-opacity="0.08" stroke="#22C55E" stroke-width="1"/>
<text x="350" y="216" font-family="JetBrains Mono, monospace" font-size="9" fill="#22C55E" text-anchor="middle">Agent 4</text>
<text x="350" y="228" font-family="JetBrains Mono, monospace" font-size="8" fill="#94a3b8" text-anchor="middle">infra</text>
<line x1="322" y1="204" x2="236" y2="190" stroke="#22C55E" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
<!-- Agent 5 — deploy -->
<circle cx="200" cy="330" r="32" fill="#22C55E" fill-opacity="0.08" stroke="#22C55E" stroke-width="1"/>
<text x="200" y="326" font-family="JetBrains Mono, monospace" font-size="9" fill="#22C55E" text-anchor="middle">Agent 5</text>
<text x="200" y="338" font-family="JetBrains Mono, monospace" font-size="8" fill="#94a3b8" text-anchor="middle">deploy</text>
<line x1="200" y1="298" x2="200" y2="220" stroke="#22C55E" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
<!-- Scope labels -->
<rect x="10" y="98" width="140" height="20" rx="4" fill="#0A1628"/>
<text x="80" y="112" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#94a3b8" text-anchor="middle">github ssh gitlab</text>
<rect x="250" y="98" width="140" height="20" rx="4" fill="#0A1628"/>
<text x="320" y="112" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#94a3b8" text-anchor="middle">twitter slack discord</text>
<rect x="0" y="256" width="100" height="20" rx="4" fill="#0A1628"/>
<text x="50" y="270" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#94a3b8" text-anchor="middle">stripe plaid</text>
<rect x="300" y="256" width="100" height="20" rx="4" fill="#0A1628"/>
<text x="350" y="270" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#94a3b8" text-anchor="middle">aws k8s docker</text>
<rect x="150" y="296" width="100" height="16" rx="4" fill="#0A1628"/>
<text x="200" y="308" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#94a3b8" text-anchor="middle">vercel netlify</text>
</svg>
</div>
</div>
</div>
</div>
</section>
<!-- Hosted CTA -->
<section class="py-20 px-6 border-t border-white/5">
<div class="max-w-2xl mx-auto text-center">
<h2 class="text-3xl font-bold text-white mb-4">Don't want to run it yourself?</h2>
<p class="text-gray-400 mb-3">We host vault<span class="text-accent font-mono">1984</span> on TIER III infrastructure across four regions. $12/year. Pick your region at signup.</p>
<p class="text-gray-500 text-sm mb-8">Your L2 keys are derived in your browser. We mathematically cannot read your private fields.</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a href="/hosted" class="bg-accent hover:bg-accent-hover text-black font-semibold px-8 py-3 rounded-lg transition-colors">See hosted plans &rarr;</a>
<a href="/install.html" class="border border-gray-600 text-gray-300 hover:border-accent hover:text-white font-medium px-8 py-3 rounded-lg transition-colors">Self-host guide</a>
</div>
</div>
</section>
<!-- Install -->
<section class="py-20 px-6 border-t border-white/5">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold text-white text-center mb-4">Up and running in 30 seconds</h2>
<p class="text-gray-400 text-center mb-14 max-w-2xl mx-auto">One command. No dependencies.</p>
<div class="max-w-3xl mx-auto space-y-6">
<div class="bg-navy-light rounded-xl p-6 font-mono text-sm overflow-x-auto border border-white/5">
<div class="text-gray-500 text-xs mb-3 font-sans">Terminal</div>
<div class="space-y-1">
<div><span class="text-gray-500"># Self-host in 30 seconds</span></div>
<div><span class="text-accent">$</span> curl -fsSL vault1984.com/install.sh | sh</div>
<div><span class="text-accent">$</span> vault1984</div>
<div class="text-gray-500"># Running on http://localhost:1984</div>
</div>
</div>
<div class="bg-navy-light rounded-xl p-6 font-mono text-sm overflow-x-auto border border-white/5">
<div class="text-gray-500 text-xs mb-3 font-sans">MCP config for Claude Code / Cursor / Codex</div>
<pre class="text-gray-300 leading-relaxed"><code>{
"mcpServers": {
"vault1984": {
"url": "http://localhost:1984/mcp",
"headers": {
"Authorization": "Bearer <span class="text-accent">mcp_your_token_here</span>"
}
}
}
}</code></pre>
</div>
</div>
<div class="text-center mt-8">
<a href="/install.html" class="text-accent hover:text-white font-medium transition-colors">Full install guide &rarr;</a>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="border-t border-white/5 py-12 px-6">
<div class="max-w-7xl mx-auto flex flex-col md:flex-row items-center justify-between gap-6">
<div class="flex items-center gap-6">
<a href="/" class="font-mono font-semibold text-lg text-white tracking-tight leading-none">vault<span class="text-accent font-mono">1984</span></a>
<div class="flex items-center gap-4 text-sm text-gray-500">
<a href="#" class="hover:text-gray-300 transition-colors">GitHub</a>
<a href="#" class="hover:text-gray-300 transition-colors">Discord</a>
<a href="#" class="hover:text-gray-300 transition-colors">X</a>
</div>
</div>
<div class="flex items-center gap-6 text-sm text-gray-500">
<a href="/privacy.html" class="hover:text-gray-300 transition-colors">Privacy</a>
<a href="/terms.html" class="hover:text-gray-300 transition-colors">Terms</a>
<span>MIT License</span>
</div>
</div>
<div class="max-w-7xl mx-auto mt-8 text-center text-xs text-gray-600">
Built for humans with AI assistants.
</div>
</footer>
<script>
document.getElementById('mobile-menu-btn').addEventListener('click', function() {
document.getElementById('mobile-menu').classList.toggle('hidden');
});
</script>
<script>
(function() {
const W = 1000, H = 460;
function project(lon, lat) {
const latR = Math.min(Math.abs(lat), 85) * Math.PI / 180 * (lat < 0 ? -1 : 1);
const miller = 1.25 * Math.log(Math.tan(Math.PI/4 + 0.4*latR));
const maxMiller = 1.25 * Math.log(Math.tan(Math.PI/4 + 0.4*80*Math.PI/180));
const x = (lon + 180) / 360 * W;
const y = H/2 - (miller / (2*maxMiller)) * H;
return [Math.round(x*10)/10, Math.round(y*10)/10];
}
function addVisitorDot(lat, lon, city) {
const svg = document.getElementById('worldmap');
if (!svg) return;
const [x, y] = project(lon, lat);
const ns = 'http://www.w3.org/2000/svg';
// Pulse ring
const ring = document.createElementNS(ns, 'circle');
ring.setAttribute('cx', x); ring.setAttribute('cy', y);
ring.setAttribute('r', '3'); ring.setAttribute('fill', 'none');
ring.setAttribute('stroke', '#EF4444'); ring.setAttribute('stroke-width', '1.5');
const a1 = document.createElementNS(ns, 'animate');
a1.setAttribute('attributeName', 'r'); a1.setAttribute('values', '3;16;3');
a1.setAttribute('dur', '2s'); a1.setAttribute('repeatCount', 'indefinite');
const a2 = document.createElementNS(ns, 'animate');
a2.setAttribute('attributeName', 'stroke-opacity'); a2.setAttribute('values', '0.8;0;0.8');
a2.setAttribute('dur', '2s'); a2.setAttribute('repeatCount', 'indefinite');
ring.appendChild(a1); ring.appendChild(a2);
// Dot
const dot = document.createElementNS(ns, 'circle');
dot.setAttribute('cx', x); dot.setAttribute('cy', y);
dot.setAttribute('r', '4'); dot.setAttribute('fill', '#EF4444');
dot.setAttribute('stroke', '#0a1628'); dot.setAttribute('stroke-width', '1.5');
// Label
const label = document.createElementNS(ns, 'text');
label.setAttribute('x', x); label.setAttribute('y', y + 15);
label.setAttribute('font-family', 'Inter,sans-serif');
label.setAttribute('font-size', '10');
label.setAttribute('fill', '#EF4444');
label.setAttribute('text-anchor', 'middle');
label.setAttribute('font-weight', '500');
label.textContent = city || 'You';
svg.appendChild(ring);
svg.appendChild(dot);
svg.appendChild(label);
}
function handleGeoData(d) {
if (!d.latitude || !d.longitude) return;
addVisitorDot(d.latitude, d.longitude, d.city || 'You');
const grid = document.getElementById('dc-grid');
if (!grid) return;
// Build visitor card
const flag = d.country_code ? d.country_code.toUpperCase().split('').map(c =>
String.fromCodePoint(c.charCodeAt(0) + 127397)).join('') : '📍';
const label = [d.city, d.country_name].filter(Boolean).join(', ') || 'Your location';
const region = d.region || '';
const card = document.createElement('div');
card.className = 'rounded-xl p-5 text-center card-hover';
card.setAttribute('data-lon', d.longitude);
card.style.cssText = 'background:#1f0a0a;border:1px solid rgba(239,68,68,0.35)';
card.innerHTML = `
<div class="text-2xl mb-2">${flag}</div>
<div class="text-white font-semibold text-sm">${label}</div>
<div class="text-gray-500 text-xs mb-2">${region}</div>
<div class="flex items-center justify-center gap-1.5 text-xs text-gray-400">
<span class="w-1.5 h-1.5 rounded-full inline-block" style="background:#EF4444;opacity:0.6"></span>You are here
</div>`;
// Expand to 5 columns
grid.style.gridTemplateColumns = "repeat(5,1fr)";
// Insert at correct longitude position
const cards = [...grid.children];
const insertBefore = cards.find(c => parseFloat(c.getAttribute('data-lon')) > d.longitude);
if (insertBefore) grid.insertBefore(card, insertBefore);
else grid.appendChild(card);
}
fetch('/geo')
.then(r => r.json())
.then(d => {
if (d.latitude) {
handleGeoData(d);
} else if (d.private && navigator.geolocation) {
navigator.geolocation.getCurrentPosition(pos => {
const lat = pos.coords.latitude, lon = pos.coords.longitude;
// Reverse geocode via open-meteo's free geocoding isn't ideal;
// use bigdatacloud free reverse geocode — no key, no signup
fetch(`/geo?lat=${lat}&lon=${lon}`)
.then(r => r.json())
.then(g => handleGeoData({
latitude: lat, longitude: lon,
city: g.city || 'You',
region: g.region || '',
country_name: g.country_name || '',
country_code: g.country_code || ''
}))
.catch(() => handleGeoData({ latitude: lat, longitude: lon,
city: 'You', region: '', country_name: '', country_code: '' }));
}, () => {});
}
})
.catch(() => {});
})();
</script>
</body>
</html>