Self-hosted, zero-knowledge encrypted, self-destructing secrets
zKettle
Self-hosted zero-knowledge expiring secrets. Encrypt locally, store ciphertext on the server, share a URL with the decryption key in the fragment. The server never sees the plaintext or the key.
Installation
Go install (requires Go 1.25+)
go install github.com/benderterminal/zkettle@latest
This installs to $GOPATH/bin (typically ~/go/bin). Make sure it's in your PATH: export PATH="$HOME/go/bin:$PATH"
Binary download
curl -fsSL https://github.com/benderterminal/zkettle/releases/latest/download/zkettle-$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') -o /usr/local/bin/zkettle && chmod +x /usr/local/bin/zkettle
From source
git clone https://github.com/benderterminal/zkettle.git && cd zkettle && make install
Via AI agent
Paste this prompt into Claude Code, Cursor, or any MCP-compatible agent:
I want to set up zKettle — a self-hosted zero-knowledge secret sharing tool. Install it with go install github.com/benderterminal/zkettle@latest, then read the MCP setup instructions in the README at https://github.com/benderterminal/zkettle. When configuring the MCP server, use the absolute path to the installed binary (find it with which zkettle or check ~/go/bin/). Once configured, test the full workflow using the CLI: create a secret, read it back, and revoke it. Note that MCP servers are loaded at startup — the new tools won't be available until the next terminal session.
Quick Start
# Start the server with a Cloudflare tunnel (instant public URL)
zkettle serve --tunnel
# Or start locally
zkettle serve --port 3000
# Create a secret (in another terminal)
echo "my secret password" | zkettle create --views 1 --minutes 60
# → http://localhost:3000/s/abc123#key
# Read a secret
zkettle read "http://localhost:3000/s/abc123#key"
# → my secret password (use -c for clipboard, -o <file> for file output)
# Revoke a secret
zkettle revoke --server http://localhost:3000 --token <delete-token> <id>
Open the URL in a browser to reveal the secret via the web viewer.
Docker Deployment
# Build and run with Docker Compose
docker compose up -d
# Or build and run manually
docker build -t zkettle .
docker run -d -p 3000:3000 -v zkettle-data:/data zkettle
The container listens on port 3000 and stores data in /data. Configure with environment variables (see Configuration Reference).
Production Deployment
With TLS (direct)
zkettle serve --host 0.0.0.0 --tls-cert /path/to/cert.pem --tls-key /path/to/key.pem
With a reverse proxy (recommended)
Run zkettle behind Caddy, Nginx, or Traefik for automatic TLS:
zkettle serve --host 127.0.0.1 --trust-proxy
Enable --trust-proxy so zkettle reads the real client IP from X-Forwarded-For headers.
Systemd
Copy the service template and enable it:
sudo cp contrib/zkettle.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now zkettle
The service template uses DynamicUser=yes with ReadWritePaths=/var/lib/zkettle, so systemd manages the data directory automatically.
Configure via environment file at /etc/zkettle/env:
ZKETTLE_PORT=3000
ZKETTLE_HOST=0.0.0.0
ZKETTLE_ADMIN_TOKEN=your-secret-token
ZKETTLE_TRUST_PROXY=true
Backups
The database is a single SQLite file at <data-dir>/zkettle.db. Back it up with:
sqlite3 /var/lib/zkettle/zkettle.db ".backup /backups/zkettle-$(date +%Y%m%d).db"
Admin API
Enable the admin endpoint by setting an admin token via environment variable:
export ZKETTLE_ADMIN_TOKEN=my-secret-admin-token
zkettle serve
Note: Passing
--admin-tokenon the command line exposes the token in process listings (ps,/proc/*/cmdline). Prefer the environment variable or config file.
List active secrets
# Via CLI
zkettle list --server http://localhost:3000 --admin-token my-secret-admin-token
# Via API
curl -H "Authorization: Bearer my-secret-admin-token" http://localhost:3000/api/admin/secrets
Returns metadata only (ID, views remaining, timestamps). No encrypted content or decryption keys are ever exposed.
GET /api/admin/secrets
Returns 404 when no admin token is configured (endpoint disabled). Requires Authorization: Bearer <token> header.
Response (200):
[
{
"id": "abc123...",
"views_left": 2,
"expires_at": "2024-01-02T03:04:05Z",
"created_at": "2024-01-01T00:00:00Z"
}
]
Metrics
Enable the /metrics endpoint with the --metrics flag:
export ZKETTLE_ADMIN_TOKEN=my-secret-admin-token
zkettle serve --metrics
The /metrics endpoint requires the admin token (Authorization: Bearer <token> header). Returns 404 when no admin token is configured.
Returns JSON metrics at GET /metrics:
{
"zkettle_secrets_active": 5
}
MCP Setup
zKettle includes an MCP server for u
Tools (3)
create_secretCreates a new encrypted secret with specified view limits and expiration time.read_secretReads and decrypts a secret from a provided URL.revoke_secretRevokes an existing secret using its ID and admin token.Environment Variables
ZKETTLE_ADMIN_TOKENToken required for administrative actions like listing secrets or accessing metrics.ZKETTLE_PORTPort for the server to listen on.ZKETTLE_HOSTHost address for the server.ZKETTLE_TRUST_PROXYEnables reading client IP from X-Forwarded-For headers.Configuration
{"mcpServers": {"zkettle": {"command": "zkettle", "args": ["mcp"]}}}