clavitor/clavitor.ai/templates/install.tmpl

158 lines
7.4 KiB
Cheetah

{{define "install-head"}}{{end}}
{{define "install"}}
<div class="hero container">
<p class="label mb-3">Open source &middot; Elastic License 2.0</p>
<h1 class="mb-4">Self-host Clavitor</h1>
<p class="lead">One binary. No Docker. No Postgres. No Redis. Runs anywhere.</p>
</div>
<hr class="divider">
<div class="section container">
<div class="step">
<div class="step-num">1</div>
<div class="step-body">
<h2>Download</h2>
<p>Pick your platform. One binary, no dependencies.</p>
<div id="dl-list" class="mb-3">
<p class="text-sm text-tertiary">Loading downloads…</p>
</div>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-body">
<h2>Run it</h2>
<div class="code-block">
<div><span class="prompt">$</span> chmod +x clavitor-linux-amd64</div>
<div><span class="prompt">$</span> ./clavitor-linux-amd64</div>
<div class="comment">Clavitor running on https://localhost</div>
</div>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-body">
<h2>Create an agent</h2>
<p>Open the web UI, go to <strong>Agents</strong>, and create a new agent. Give it a name (e.g. "Claude Code") and choose which entries it can access. Clavitor generates a setup token &mdash; a single string that encodes the vault address, agent identity, and encryption key.</p>
<p class="mt-3">On the machine where your AI agent runs, initialize the CLI with that token:</p>
<div class="code-block">
<div><span class="prompt">$</span> clavitor-cli init &lt;setup-token&gt;</div>
</div>
<p class="mt-3 text-sm">One-time setup. The token is decoded and saved as encrypted local config.</p>
<div class="mt-4" style="background:#f5f5f5;border:1px solid #e5e5e5;border-radius:var(--radius-sm);padding:24px;text-align:center">
<p class="text-sm text-tertiary" style="margin:0">Screenshot placeholder &mdash; agent creation UI</p>
</div>
</div>
</div>
<div class="step">
<div class="step-num">4</div>
<div class="step-body">
<h2>Install the Claude Code skill</h2>
<p>The CLI ships with a built-in skill definition that teaches Claude Code how to use your vault. One command installs it.</p>
<div class="code-block">
<div><span class="comment"># Install globally (all projects)</span></div>
<div><span class="prompt">$</span> clavitor-cli skill > ~/.claude/skills/clavitor.md</div>
<div class="mt-2"><span class="comment"># Or install for a specific project</span></div>
<div><span class="prompt">$</span> clavitor-cli skill > /path/to/project/.claude/skills/clavitor.md</div>
</div>
<p class="mt-3 text-sm">The skill is embedded in the binary. Update it by downloading a new release.</p>
</div>
</div>
<div class="step">
<div class="step-num">5</div>
<div class="step-body">
<h2>Use it</h2>
<p>Your agent can now fetch credentials, generate TOTP codes, and store new secrets. Every access is logged in the vault's audit trail.</p>
<div class="code-block">
<div><span class="comment"># Fetch a credential</span></div>
<div><span class="prompt">$</span> clavitor-cli get github</div>
<div class="mt-2"><span class="comment"># Generate a TOTP code</span></div>
<div><span class="prompt">$</span> clavitor-cli totp github</div>
<div class="mt-2"><span class="comment"># Store a new credential</span></div>
<div><span class="prompt">$</span> clavitor-cli put credential "AWS Prod" --username admin --password s3cret</div>
<div class="mt-2"><span class="comment"># List all entries</span></div>
<div><span class="prompt">$</span> clavitor-cli list</div>
</div>
</div>
</div>
<hr class="divider mb-8 mt-4">
<h2 class="mb-4">Run as a service</h2>
<p class="mb-4">For always-on availability, run Clavitor as a systemd service.</p>
<p class="label mb-3">/etc/systemd/system/clavitor.service</p>
<div class="code-block mb-4"><pre>[Unit]
Description=clavitor
After=network.target
[Service]
Type=simple
User=clavitor
ExecStart=/usr/local/bin/clavitor
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target</pre></div>
<div class="code-block mb-8"><span class="prompt">$</span> sudo systemctl enable --now clavitor</div>
<h2 class="mb-4">Expose to the internet</h2>
<p class="mb-4">Put Clavitor behind Caddy for TLS and remote access.</p>
<p class="label mb-3">Caddyfile</p>
<div class="code-block"><pre>vault.yourdomain.com {
reverse_proxy localhost:1984
}</pre></div>
<hr class="divider mb-8 mt-4">
<h2 class="mb-4">Self-hosting and AI agents</h2>
<p class="mb-4">Your vault should not run on the same machine as your AI agents.</p>
<p class="mb-4">AI agents have shell access. If the vault database is on the same filesystem, an agent can read it directly &mdash; bypassing the API, the audit log, and the encryption model.</p>
<p class="mb-4">Run the vault on a separate device. A Raspberry Pi, a NAS, a VM, or a VPS &mdash; anything the agent can reach over HTTPS but cannot SSH into. Even a $5/month VPS works, though <a href="/hosted">hosted Clavitor</a> does the same thing for $1/month.</p>
<p>If you must run on the same machine: create a dedicated system user for the vault, restrict the database file to that user (<code>chmod 600</code>), and run the vault as a systemd service under that account. This is not equivalent to network isolation, but it raises the bar.</p>
<hr class="divider mb-8 mt-4">
<h2 class="mb-4">Rather not manage it yourself?</h2>
<p class="lead mb-6">Same vault, same features. We handle updates, backups, and TLS. <s>$20</s> $12/yr.</p>
<a href="/hosted" class="btn btn-primary">See hosted option &rarr;</a>
</div>
{{end}}
{{define "install-script"}}
<script>
(function() {
function fmtSize(bytes) {
return (bytes / 1024 / 1024).toFixed(1) + ' MB';
}
function fmtName(name) {
return name.replace('clavitor-', '').replace('-', '/');
}
fetch('/downloads/api')
.then(r => r.json())
.then(files => {
const el = document.getElementById('dl-list');
if (!files || !files.length) { el.innerHTML = '<p class="text-sm text-tertiary">No downloads available yet.</p>'; return; }
el.innerHTML = files.map(f =>
'<div style="margin-bottom:12px">' +
'<a href="/download/' + f.name + '" class="btn btn-ghost btn-sm btn-mono">' + fmtName(f.name) + '</a> ' +
'<span class="text-sm text-tertiary">' + fmtSize(f.size) + '</span>' +
'<div class="mono text-sm text-tertiary" style="margin-top:4px;word-break:break-all">sha256: ' + f.sha256 + '</div>' +
'</div>'
).join('');
})
.catch(() => {
document.getElementById('dl-list').innerHTML = '<p class="text-sm text-tertiary">Could not load downloads.</p>';
});
})();
</script>
{{end}}