Self-hosted, zero-knowledge password vault. Single Go binary serves the API and the embedded React SPA. Browser extension and CLI talk to the same server. AGPL-3.0.
- Zero-knowledge by construction. Argon2id key derivation and AES-256-GCM happen in the browser worker, the extension, or the CLI. The server has no code path to decrypt.
- One binary, one image. The web bundle is embedded into the Go binary. Final container image is ~45MB on
gcr.io/distroless/static-debian12:nonroot. - Multi-user from day 1. Organizations, invites, role-based vault sharing, RSA-OAEP wrap with Ed25519 signature pinning, member-removal triggers a vault rekey.
- Supply-chain hardened. goreleaser builds for linux/darwin/windows × amd64/arm64; cosign keyless signing on every artifact; CycloneDX SBOM per archive; SLSA-L3 provenance attestation on public releases.
Docs: vaultctl.vinelab.in
- Zero-knowledge. Argon2id + AES-256-GCM run client-side; the server only ever stores ciphertext.
- Multi-vault + sharing. Personal and shared vaults, role-based access, RSA-OAEP recipient key-wrap with Ed25519 signature pinning; removing a member rekeys the vault.
- Browser extension (MV3). Autofill and save for logins, plus capture and fill of credit cards and identities; multi-vault switcher and cross-vault autofill.
- TOTP codes. Store 2FA secrets and read live one-time codes in the app.
- Password health. Weak/reused/old detection, with an optional Have I Been Pwned breach check (k-anonymity, client-side, off by default).
- Import / export. Including CSV, alongside the encrypted JSON export.
- Encrypted cloud backups. Per-user scheduled, double-sealed backups to local, S3, WebDAV, Google Drive, Dropbox, or OneDrive.
- Email. Optional SMTP for signup verification, new-device login alerts, and activity digests.
- Activity log. Self-audit trail of security-relevant events.
- Admin / orgs. Organizations, invites, member roles.
- i18n. English and German across the web app and extension.
- 2FA. TOTP for every user, with optional server-wide enforcement.
vaultctl is configured entirely through VAULTCTL_ environment variables (see .env.example). Full references:
docs/setup/configuration.md- every env var, defaults, and which are required in production.docs/setup/email.md- SMTP setup and what enabling mail turns on.docs/setup/backup-sync.md- encrypted per-user cloud backups and OAuth provider registration.
git clone https://github.com/vineethkrishnan/vaultctl.git
cd vaultctl
cp .env.example .env
# fill in every secret - server fail-closes if any prod secret is empty.
# generate values with: openssl rand -base64 32 (or 64 for JWT secrets)
docker compose up -d # starts caddy + vaultctl + postgres; migrations run automatically on startupOpen https://${VAULTCTL_BASE_URL} and register the first user. Without a TLS-terminating proxy, use docker-compose.simple.yml and front it with your own reverse proxy on 127.0.0.1:8080.
Step-by-step screenshots of the registration -> recovery-kit -> first-item flow: docs/setup/walkthrough.md.
The bundled compose sets VAULTCTL_DB_SSL_INSECURE_OK=true because Postgres lives on a private bridge network. For any deploy where the DB is reachable across hosts, leave this unset and configure VAULTCTL_DB_SSL_MODE=verify-full.
go install github.com/vineethkrishnan/vaultctl/cmd/server@latest
# or grab a signed binary from the latest release
export VAULTCTL_API_URL=https://vault.example.com
vaultctl login
vaultctl ls
vaultctl get GitHub
vaultctl add login --name Reddit
vaultctl backup --output /var/backups/vaultctlThe same binary runs the server (vaultctl server), applies migrations (vaultctl migrate up|down), and runs the client commands. --json is honored on every read command.
cd extension
npm ci
npm run build # outputs .output/chrome-mv3Load extension/.output/chrome-mv3 via chrome://extensions -> Developer mode -> Load unpacked. Firefox: about:debugging.
Toolchain: Go 1.22+, Node 22+, Docker (with docker compose), and GNU make. The make targets below are thin wrappers around go build / npm / golangci-lint / gosec / govulncheck - install any tool the target needs that you don't already have.
make web-build # build the SPA (embedded into the Go binary)
make build # build the vaultctl binary
make run # run the server locally on :8080
make test # go test ./... -race -count=1 -coverprofile=coverage.out
make lint # golangci-lint run ./...
make sec # gosec + govulncheckWeb dev server (proxies /api to http://localhost:8080):
cd web
npm ci
npm run dev # http://localhost:5173
npm run typecheck # tsc --noEmit
npm run test # vitest
npm run test:e2e # playwright (35 e2e tests)vaultctl publishes four layers of supply-chain evidence. For credential-vault use you should verify at least the layer matching how you install. Step-by-step verification commands: docs/security/verifying-releases.md.
| Layer | What it proves |
|---|---|
cosign verify-blob against checksums.txt |
the binary tarball matches what the release workflow built |
cosign verify against the container manifest |
the image digest you pulled was produced by the release workflow |
| SLSA build provenance attestation | a named GitHub workflow at a specific tag produced the artifact |
CycloneDX SBOM (*.sbom.cdx.json) |
complete dependency inventory for review and vuln scanning |
cmd/server/ # binary entry: server, migrate, backup, admin, client cmds
internal/domain/ # core types and invariants - no I/O
internal/application/ # use cases composed from ports
internal/infrastructure/ # postgres, JWT, crypto adapters
internal/presenters/api/ # chi router, handlers, middleware
internal/presenters/cli/ # cobra command tree
migrations/ # *.sql, embedded into the binary
web/ # React + Vite SPA, embedded into the binary
extension/ # WXT + MV3 browser extension
deploy/caddy/ # Caddyfile for the bundled stack
docs/security/ # release verification guide
docs-site/ # public docs site (deployed to Cloudflare Pages)
AGPL-3.0. See LICENSE.