623 lines
38 KiB
Cheetah
623 lines
38 KiB
Cheetah
{{define "index"}}
|
|
<!-- Hero -->
|
|
<div class="container hero-split">
|
|
<div>
|
|
<p class="label accent mb-6">George Orwell — 1984</p>
|
|
<h1 class="mb-6">"If you want to keep a secret, you must also hide it from yourself."</h1>
|
|
<p class="lead mb-6">We did. Your Sealed 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="btn-row">
|
|
<a href="/hosted" class="btn btn-primary">Get hosted — <s>$20</s> $12/yr</a>
|
|
<a href="/install" class="btn btn-ghost">Self-host free →</a>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<!-- Hero SVG: L2/L3 split diagram -->
|
|
<svg viewBox="0 0 480 380" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<!-- Background -->
|
|
<rect x="0" y="0" width="480" height="380" rx="12" fill="#f5f5f5"/>
|
|
|
|
<!-- Top labels -->
|
|
<text x="130" y="35" font-family="JetBrains Mono, monospace" font-size="11" fill="#737373" text-anchor="middle">AI Agent</text>
|
|
<text x="350" y="35" font-family="JetBrains Mono, monospace" font-size="11" fill="#737373" 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>
|
|
|
|
<!-- L2 Column (AI-readable) -->
|
|
<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">L2 — AI can read</text>
|
|
|
|
<!-- L2 items -->
|
|
<g>
|
|
<rect x="50" y="115" width="160" height="36" rx="6" fill="#ffffff"/>
|
|
<text x="80" y="138" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="80" y="186" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="80" y="234" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="80" y="282" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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>
|
|
|
|
<!-- L3 Column (sealed) -->
|
|
<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">L3 — you only</text>
|
|
|
|
<!-- L3 items -->
|
|
<g>
|
|
<rect x="270" y="115" width="160" height="36" rx="6" fill="#ffffff"/>
|
|
<text x="300" y="138" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="300" y="186" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="300" y="234" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#ffffff"/>
|
|
<text x="300" y="282" font-family="JetBrains Mono, monospace" font-size="11" fill="#525252">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="#f5f5f5" stroke="#737373" stroke-width="1"/>
|
|
<circle cx="240" cy="352" r="3" fill="none" stroke="#737373" stroke-width="1"/>
|
|
<line x1="240" y1="355" x2="240" y2="360" stroke="#737373" stroke-width="1"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- 3D Box Prototypes (TEMPORARY) -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">Black box icon options</h2>
|
|
<p class="lead mb-8">6 corners. Front face dominant, slight depth peek right + top.</p>
|
|
<div style="display:flex;gap:48px;align-items:flex-end;flex-wrap:wrap">
|
|
|
|
<!-- Option A: Subtle depth, strong contrast -->
|
|
<div style="text-align:center">
|
|
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,15 58,15 58,73 4,73" fill="#0A0A0A"/>
|
|
<polygon points="4,15 14,8 68,8 58,15" fill="#555"/>
|
|
<polygon points="58,15 68,8 68,66 58,73" fill="#333"/>
|
|
</svg>
|
|
<p class="text-sm mt-3"><strong>A.</strong> Subtle</p>
|
|
</div>
|
|
|
|
<!-- Option B: More depth, strong contrast -->
|
|
<div style="text-align:center">
|
|
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,18 54,18 54,74 4,74" fill="#0A0A0A"/>
|
|
<polygon points="4,18 18,8 68,8 54,18" fill="#555"/>
|
|
<polygon points="54,18 68,8 68,64 54,74" fill="#333"/>
|
|
</svg>
|
|
<p class="text-sm mt-3"><strong>B.</strong> More depth</p>
|
|
</div>
|
|
|
|
<!-- Option C: Just a hair -->
|
|
<div style="text-align:center">
|
|
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,13 62,13 62,73 4,73" fill="#0A0A0A"/>
|
|
<polygon points="4,13 11,8 69,8 62,13" fill="#555"/>
|
|
<polygon points="62,13 69,8 69,68 62,73" fill="#333"/>
|
|
</svg>
|
|
<p class="text-sm mt-3"><strong>C.</strong> Just a hair</p>
|
|
</div>
|
|
|
|
<!-- Option D: Very light top face -->
|
|
<div style="text-align:center">
|
|
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,15 58,15 58,73 4,73" fill="#0A0A0A"/>
|
|
<polygon points="4,15 14,8 68,8 58,15" fill="#777"/>
|
|
<polygon points="58,15 68,8 68,66 58,73" fill="#444"/>
|
|
</svg>
|
|
<p class="text-sm mt-3"><strong>D.</strong> Lighter top</p>
|
|
</div>
|
|
|
|
<!-- Option E: A at icon size 40px -->
|
|
<div style="text-align:center">
|
|
<div style="width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center">
|
|
<svg width="40" height="40" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,15 58,15 58,73 4,73" fill="#0A0A0A"/>
|
|
<polygon points="4,15 14,8 68,8 58,15" fill="#555"/>
|
|
<polygon points="58,15 68,8 68,66 58,73" fill="#333"/>
|
|
</svg>
|
|
</div>
|
|
<p class="text-sm mt-3"><strong>E.</strong> A at 40px</p>
|
|
</div>
|
|
|
|
<!-- Option F: Red variant at 40px -->
|
|
<div style="text-align:center">
|
|
<div style="width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center">
|
|
<svg width="40" height="40" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<polygon points="4,15 58,15 58,73 4,73" fill="#DC2626"/>
|
|
<polygon points="4,15 14,8 68,8 58,15" fill="#F87171"/>
|
|
<polygon points="58,15 68,8 68,66 58,73" fill="#B91C1C"/>
|
|
</svg>
|
|
</div>
|
|
<p class="text-sm mt-3"><strong>F.</strong> Red at 40px</p>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- The Problem -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">The problem</h2>
|
|
<p class="lead mb-8">Every password manager was built before AI agents existed. Now they need to catch up.</p>
|
|
<div class="grid-3">
|
|
<div class="card card-hover">
|
|
<div class="feature-icon red"><svg 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="mb-3">All-or-nothing is broken</h3>
|
|
<p>All others give your AI agent access to everything in your vault, or nothing at all. Your AI needs your GitHub token — it shouldn't also see your passport number.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon red"><svg 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="mb-3">Policy isn't security</h3>
|
|
<p>"AI-safe" vaults still decrypt everything server-side. If the server can read it, it's not truly private. Math beats policy every time.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon red"><svg 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="mb-3">Agents need credentials — and 2FA</h3>
|
|
<p>Your AI can't log in, pass two-factor, or rotate keys without access. <span class="vaultname">clavitor</span> lets it do all three — without exposing your credit card to the same pipeline.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- How it works -->
|
|
<div class="section container">
|
|
<p class="label mb-4">How it works</p>
|
|
<h2 class="mb-6">"Your assistant can book your flights.<br><span class="gradient-text">Not read your diary.</span>"</h2>
|
|
<p class="lead mb-8">Every field is encrypted. But some get a second lock. That second key is derived from your fingerprint and only exists in your browser. We hold the safe. Only you hold that key.</p>
|
|
<div class="grid-2">
|
|
<div class="card alt">
|
|
<span class="badge accent mb-4">Agent fields</span>
|
|
<h3 class="mb-3">AI-readable</h3>
|
|
<p class="mb-4">Encrypted at rest, decryptable by the vault server. Your AI agent reads these via MCP.</p>
|
|
<ul class="checklist">
|
|
<li>API keys & tokens</li>
|
|
<li>SSH keys</li>
|
|
<li>TOTP 2FA codes — AI generates them for you</li>
|
|
<li>OAuth tokens</li>
|
|
<li>Structured notes</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card red">
|
|
<span class="badge red mb-4">Sealed fields</span>
|
|
<h3 class="mb-3">Touch ID only</h3>
|
|
<p class="mb-4">Encrypted client-side with WebAuthn PRF. The server never sees the plaintext. Ever.</p>
|
|
<ul class="checklist red">
|
|
<li>Credit card numbers</li>
|
|
<li>CVV</li>
|
|
<li>Passport & SSN</li>
|
|
<li>Private signing keys</li>
|
|
<li>Private notes</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- Features -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">Built different</h2>
|
|
<p class="lead mb-8">Not another password manager with an AI checkbox. The architecture is the feature.</p>
|
|
<div class="grid-3">
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">Field-level AI visibility</h3>
|
|
<p>Each field has its own encryption tier. Your AI reads the username, not the CVV. Same entry, different access.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">WebAuthn PRF</h3>
|
|
<p>Sealed encryption uses WebAuthn PRF — a cryptographic key derived from your biometric hardware. Math, not policy. We literally cannot decrypt it.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">AI-powered 2FA</h3>
|
|
<p>Store TOTP secrets as Agent fields. Your AI generates time-based codes on demand via MCP — no more switching to your phone.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">Scoped MCP tokens</h3>
|
|
<p>Create separate MCP tokens per agent. Each token sees only its designated entries. Compromise one, the rest stay clean.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">One binary, one file</h3>
|
|
<p>No Docker. No Postgres. No Redis. One Go binary, one SQLite file. Runs on a Raspberry Pi. Runs on a $4/month VPS.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<div class="feature-icon"><svg 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="mb-3">LLM field mapping</h3>
|
|
<p>Import from any password manager. The built-in LLM automatically classifies which fields should be Agent vs Sealed.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- Multi-agent swarms -->
|
|
<div class="section container">
|
|
<div class="grid-2">
|
|
<div>
|
|
<h2 class="mb-4">10 agents.<br><span class="gradient-text">Each gets exactly what it needs.</span></h2>
|
|
<p class="lead mb-6">Create scoped MCP tokens per agent. One compromised agent exposes one scope — not your entire vault.</p>
|
|
<div class="code-block">
|
|
<p class="code-label">~/.claude/mcp.json</p>
|
|
<pre>{
|
|
"mcpServers": {
|
|
"vault-dev": {
|
|
"url": "http://localhost:1984/mcp",
|
|
"headers": { "Authorization": "Bearer <span class="prompt">mcp_dev_a3f8...</span>" }
|
|
},
|
|
"vault-social": {
|
|
"url": "http://localhost:1984/mcp",
|
|
"headers": { "Authorization": "Bearer <span class="prompt">mcp_social_7b2e...</span>" }
|
|
}
|
|
}
|
|
}</pre>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<!-- Multi-agent SVG -->
|
|
<svg viewBox="0 0 400 360" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<!-- Center vault -->
|
|
<rect x="160" y="140" width="80" height="80" rx="12" fill="#f5f5f5" stroke="#737373" stroke-width="1.5"/>
|
|
<rect x="175" y="152" width="20" height="20" rx="0" fill="#0A0A0A"/>
|
|
<text x="200" y="190" font-family="Figtree, sans-serif" font-size="10" fill="#0A0A0A" text-anchor="middle" font-weight="700" letter-spacing="0.25em">CLAVITOR</text>
|
|
|
|
<!-- Agent 1 — dev -->
|
|
<circle cx="80" cy="60" r="32" fill="#0A0A0A" fill-opacity="0.08" stroke="#0A0A0A" stroke-width="1"/>
|
|
<text x="80" y="56" font-family="JetBrains Mono, monospace" font-size="9" fill="#0A0A0A" text-anchor="middle">Agent 1</text>
|
|
<text x="80" y="68" font-family="JetBrains Mono, monospace" font-size="8" fill="#737373" text-anchor="middle">dev</text>
|
|
<line x1="108" y1="80" x2="165" y2="145" stroke="#0A0A0A" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
|
|
|
|
<!-- Agent 2 — social -->
|
|
<circle cx="320" cy="60" r="32" fill="#0A0A0A" fill-opacity="0.08" stroke="#0A0A0A" stroke-width="1"/>
|
|
<text x="320" y="56" font-family="JetBrains Mono, monospace" font-size="9" fill="#0A0A0A" text-anchor="middle">Agent 2</text>
|
|
<text x="320" y="68" font-family="JetBrains Mono, monospace" font-size="8" fill="#737373" text-anchor="middle">social</text>
|
|
<line x1="292" y1="80" x2="235" y2="145" stroke="#0A0A0A" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
|
|
|
|
<!-- Agent 3 — finance -->
|
|
<circle cx="50" cy="220" r="32" fill="#0A0A0A" fill-opacity="0.08" stroke="#0A0A0A" stroke-width="1"/>
|
|
<text x="50" y="216" font-family="JetBrains Mono, monospace" font-size="9" fill="#0A0A0A" text-anchor="middle">Agent 3</text>
|
|
<text x="50" y="228" font-family="JetBrains Mono, monospace" font-size="8" fill="#737373" text-anchor="middle">finance</text>
|
|
<line x1="78" y1="204" x2="164" y2="190" stroke="#0A0A0A" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
|
|
|
|
<!-- Agent 4 — infra -->
|
|
<circle cx="350" cy="220" r="32" fill="#0A0A0A" fill-opacity="0.08" stroke="#0A0A0A" stroke-width="1"/>
|
|
<text x="350" y="216" font-family="JetBrains Mono, monospace" font-size="9" fill="#0A0A0A" text-anchor="middle">Agent 4</text>
|
|
<text x="350" y="228" font-family="JetBrains Mono, monospace" font-size="8" fill="#737373" text-anchor="middle">infra</text>
|
|
<line x1="322" y1="204" x2="236" y2="190" stroke="#0A0A0A" stroke-width="1" stroke-opacity="0.4" stroke-dasharray="4 3"/>
|
|
|
|
<!-- Agent 5 — deploy -->
|
|
<circle cx="200" cy="330" r="32" fill="#0A0A0A" fill-opacity="0.08" stroke="#0A0A0A" stroke-width="1"/>
|
|
<text x="200" y="326" font-family="JetBrains Mono, monospace" font-size="9" fill="#0A0A0A" text-anchor="middle">Agent 5</text>
|
|
<text x="200" y="338" font-family="JetBrains Mono, monospace" font-size="8" fill="#737373" text-anchor="middle">deploy</text>
|
|
<line x1="200" y1="298" x2="200" y2="220" stroke="#0A0A0A" 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="#ffffff"/>
|
|
<text x="80" y="112" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#737373" text-anchor="middle">github ssh gitlab</text>
|
|
|
|
<rect x="250" y="98" width="140" height="20" rx="4" fill="#ffffff"/>
|
|
<text x="320" y="112" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#737373" text-anchor="middle">twitter slack discord</text>
|
|
|
|
<rect x="0" y="256" width="100" height="20" rx="4" fill="#ffffff"/>
|
|
<text x="50" y="270" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#737373" text-anchor="middle">stripe plaid</text>
|
|
|
|
<rect x="300" y="256" width="100" height="20" rx="4" fill="#ffffff"/>
|
|
<text x="350" y="270" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#737373" text-anchor="middle">aws k8s docker</text>
|
|
|
|
<rect x="150" y="296" width="100" height="16" rx="4" fill="#ffffff"/>
|
|
<text x="200" y="308" font-family="JetBrains Mono, monospace" font-size="7.5" fill="#737373" text-anchor="middle">vercel netlify</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- Access Methods -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">Your agent and you — same vault, right access</h2>
|
|
<p class="lead mb-8">Four ways in. Each one designed for a different context. All pointing at the same encrypted store.</p>
|
|
<div class="grid-2">
|
|
<div class="card card-hover">
|
|
<p class="label accent mb-3">MCP</p>
|
|
<h3 class="mb-2">For AI agents</h3>
|
|
<p>Claude, GPT, or any MCP-compatible agent can search credentials, fetch API keys, and generate 2FA codes — scoped to exactly what you allow.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<p class="label accent mb-3">Extension</p>
|
|
<h3 class="mb-2">For humans in a browser</h3>
|
|
<p>Autofill passwords, generate 2FA codes inline, and unlock L3 fields with Touch ID — without leaving the page you're on.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<p class="label accent mb-3">CLI</p>
|
|
<h3 class="mb-2">For terminal workflows</h3>
|
|
<p>Pipe credentials directly into scripts and CI pipelines. <code>vault get github.token</code> — done.</p>
|
|
</div>
|
|
<div class="card card-hover">
|
|
<p class="label accent mb-3">API</p>
|
|
<h3 class="mb-2">For everything else</h3>
|
|
<p>REST API with scoped tokens. Give your deployment pipeline read access to staging keys. Nothing else.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- The competition -->
|
|
<div class="section container">
|
|
<p class="label mb-4">The competition</p>
|
|
<h2 class="mb-4">We listened. And addressed them all.</h2>
|
|
<p class="lead mb-8">Real complaints from real users — about 1Password, Bitwarden, and LastPass. Pulled from forums, GitHub issues, and Hacker News. Not cherry-picked from our own users.</p>
|
|
|
|
<div class="grid-3">
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">1PASSWORD — Community Forum</p>
|
|
<p><em>"The web extensions are laughably bad at this point. This has been going on for months. They either won't fill, wont' unlock, or just plain won't do anything (even clicking extension icon). It's so bad"</em></p>
|
|
<p class="mt-2"><a href="https://www.1password.community/discussions/1password/constantly-being-asked-to-unlock-with-password/90511" target="_blank" rel="noopener">— notnotjake, April 2024 ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: No desktop app dependency. The extension talks directly to the local vault binary — no IPC, no sync, no unlock chains.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">BITWARDEN — GitHub Issues</p>
|
|
<p><em>"Every single website loads slower. From Google, up to social media websites like Reddit, Instagram, X up to websites like example.com. Even scrolling and animation stutters sometimes. javascript heavy websites like X, Instagram, Reddit etc. become extremely sluggish when interacting with buttons. So for me the Bitwarden browser extension is unusable. It interferes with my browsing experience like malware."</em></p>
|
|
<p class="mt-2"><a href="https://github.com/bitwarden/clients/issues/11077" target="_blank" rel="noopener">— julianw1011, 2024 ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: Zero content scripts. The extension injects nothing into pages — it fills via the browser autofill API only when you ask.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">LASTPASS — Hacker News</p>
|
|
<p><em>"The fact they're drip-feeding how bad this breach actually was is terrible enough... Personally I'm never touching them again."</em></p>
|
|
<p class="mt-2"><a href="https://news.ycombinator.com/item?id=34516275" target="_blank" rel="noopener">— intunderflow, January 2023 ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: Self-host or use hosted with L3 encryption — we mathematically cannot read your private fields. No vault data to breach.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">1PASSWORD — Community Forum</p>
|
|
<p><em>"Since doing so, it asks me to enter my password every 10 minutes or so in the chrome extension"</em></p>
|
|
<p class="mt-2"><a href="https://www.1password.community/discussions/1password/why-does-the-chrome-extension-keep-asking-for-my-password-every-10-mins-rather-t/74253" target="_blank" rel="noopener">— Anonymous (Former Member), November 2022 ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: WebAuthn-first. Touch ID is the primary unlock. Session lives locally — no server-side expiry forcing re-auth.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">BITWARDEN — Community Forums</p>
|
|
<p><em>"the password not only auto-filled in the password field, but also auto-filled in reddit's search box!"</em></p>
|
|
<p class="mt-2"><em>"if autofill has the propensity at times to put an entire password in plain text in a random field, autofill seems like more risk than it's worth."</em></p>
|
|
<p class="mt-2"><a href="https://community.bitwarden.com/t/auto-fill-is-pasting-password-in-website-search-box/44045" target="_blank" rel="noopener">— xru1nib5 ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: LLM field mapping. The extension reads the form, asks the model which field is which — fills by intent, not by CSS selector.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card red">
|
|
<p class="label red mb-3">BITWARDEN — Community Forums</p>
|
|
<p><em>"Bitwarden REFUSES to autofill the actual password saved for a given site or app...and instead fills an old password. It simply substitutes the OLD password for the new one that is plainly saved in the vault."</em></p>
|
|
<p class="mt-2"><a href="https://community.bitwarden.com/t/autofill-is-wrong-saved-password-is-right/32090" target="_blank" rel="noopener">— gentlezacharias ↗</a></p>
|
|
<hr class="divider mt-4 mb-4">
|
|
<ul class="checklist">
|
|
<li><span class="vaultname">clavitor</span>: LLM field mapping matches by intent. Entries are indexed by URL — the right credential for the right site, every time.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<p class="mt-8">All quotes verbatim from public posts. URLs verified. <a href="/sources">View sources →</a></p>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- Hosted CTA -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">Your vault needs to be everywhere you are.</h2>
|
|
<p class="lead mb-3">A password manager that only works on your home network isn't a password manager. Your laptop moves. Your phone moves. Your browser extension needs your vault at the coffee shop, on the plane, at the client's office.</p>
|
|
<p class="mb-3">Self-hosting that means a server with a public IP, DNS, TLS certificates, uptime monitoring, and backups. That's not a weekend project — that's infrastructure.</p>
|
|
<p class="mb-8">We run <span class="vaultname">clavitor</span> across 22 regions on every continent. <s>$20</s> $12/yr. Your Sealed keys never leave your browser — we mathematically cannot read your private fields.</p>
|
|
<div class="btn-row">
|
|
<a href="/hosted" class="btn btn-primary">Get hosted →</a>
|
|
<a href="/install" class="btn btn-ghost">Self-host anyway</a>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<!-- Quick install -->
|
|
<div class="section container">
|
|
<h2 class="mb-4">Up and running in 30 seconds</h2>
|
|
<p class="lead mb-8">One command. No dependencies.</p>
|
|
<div class="code-block mb-6">
|
|
<p class="code-label">Terminal</p>
|
|
<div><span class="comment"># Self-host in 30 seconds</span></div>
|
|
<div><span class="prompt">$</span> curl -fsSL clavitor.com/install.sh | sh</div>
|
|
<div><span class="prompt">$</span> clavitor</div>
|
|
<div class="comment"># Running on http://localhost:1984</div>
|
|
</div>
|
|
<div class="code-block">
|
|
<p class="code-label">MCP config for Claude Code / Cursor / Codex</p>
|
|
<pre>{
|
|
"mcpServers": {
|
|
"clavitor": {
|
|
"url": "http://localhost:1984/mcp",
|
|
"headers": { "Authorization": "Bearer <span class="prompt">mcp_your_token_here</span>" }
|
|
}
|
|
}
|
|
}</pre>
|
|
</div>
|
|
<p class="mt-4"><a href="/install" class="btn btn-accent">Full install guide →</a></p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "index-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', '#ffffff'); 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 = 'visitor-card card-hover';
|
|
card.setAttribute('data-lon', d.longitude);
|
|
card.innerHTML = `
|
|
<div class="visitor-flag">${flag}</div>
|
|
<div class="visitor-label">${label}</div>
|
|
<div class="visitor-region">${region}</div>
|
|
<div class="visitor-status">
|
|
<span class="visitor-dot"></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>
|
|
{{end}}
|