Ephemeral and authenticated cloud storage for .env files. Open source under MIT.
- Quick share — encrypt in the browser (AES-256-GCM), store ciphertext in Upstash Redis, put the key in the URL fragment so it never hits your server.
- Cloud vault — sign in with Better Auth (email + password), organize projects, upload and view / update / delete env blobs stored in Supabase Postgres via Drizzle ORM. Payloads are encrypted at rest with AES-256-GCM using
STORAGE_ENCRYPTION_KEY.
| Maintainer | Srijan Bajracharya (srijan.bajracharya97@gmail.com) |
| Repository | github.com/lucerowb/dot-vault |
| License | MIT — free to use, modify, and self-host |
| Support | Buy Me a Coffee |
Self-hosting: This repo is deployment-agnostic. Example URLs like
dot-vault.lucerowb.cloudin docs are the maintainer’s demo instance, not a required service.
- Quick start
- Monorepo packages
- Features
- How to use
- Environment variables
- HTTP API
- CI
- Documentation
- Security notes
- Deploying
- Node.js 20+ and pnpm 9.x
- Supabase (or any Postgres):
DATABASE_URLwith?sslmode=requirein production - Upstash Redis (optional): required for ephemeral quick share and production rate limits
git clone https://github.com/lucerowb/dot-vault.git
cd dot-vault
cp .env.example .env.local
# Fill DATABASE_URL, BETTER_AUTH_SECRET, STORAGE_ENCRYPTION_KEY, Redis for quick share, etc.
pnpm install
pnpm run db:migrate # or apply drizzle/*.sql in order in the Supabase SQL editor
pnpm dev # Next.js + Docusaurus (docs at http://localhost:3000/docs/)- Home: http://localhost:3000
- Quick share:
/quick-share - Cloud vault: register →
/dashboard
If pnpm install complains about ignored build scripts (esbuild), run pnpm approve-builds once and retry.
| Command | Purpose |
|---|---|
pnpm dev |
Next.js + Docusaurus together (/docs/ on port 3000) |
pnpm dev:web |
Next.js only (needs pnpm dev:docs or prior pnpm build:docs) |
pnpm dev:docs |
Docusaurus only (port 3456) |
pnpm build:docs |
Build static docs into public/docs/ |
pnpm build / pnpm start |
Production build / server (runs build:docs first) |
pnpm lint |
ESLint |
pnpm test |
Vitest (crypto helpers) |
pnpm test:e2e |
Playwright (install browser first: pnpm exec playwright install chromium) |
pnpm build:cli |
Build packages/cli |
pnpm build:extension |
Build packages/browser-extension → dist/ |
pnpm run db:generate |
Regenerate Drizzle migrations |
pnpm run db:migrate |
Apply migrations |
pnpm run db:push |
Push schema (dev prototyping) |
pnpm workspace (see pnpm-workspace.yaml):
| Package | Path | Description |
|---|---|---|
| dot-vault (app) | . |
Next.js web app + API |
| dotvault (CLI) | packages/cli |
Terminal client for projects and env files |
| dotvault-browser-extension | packages/browser-extension |
Chrome extension for one-click fills on host platforms |
Build artifacts (dist/, node_modules/) are gitignored; CI builds them on demand.
| Feature | Summary | Details |
|---|---|---|
| Quick share | Client-side encrypted links with TTL, optional passphrase, one-time URLs, sender revoke | Quick share below |
| Cloud vault | Projects, labeled env files, encrypted at rest | Cloud vault |
| Collaboration | Invite by email (editor / viewer), accept at /invite/<token> |
Collaboration |
| Version history | Automatic versions per env, diff and rollback in the dashboard | docs/VERSION_HISTORY.md |
| Audit logs | Who did what, when, from where; export from API | docs/AUDIT_LOGS.md |
| Import / export | .env, JSON, and more via API |
docs/IMPORT_EXPORT.md |
| Secret templates | Pre-built patterns (AWS, Stripe, DB, etc.) | docs/SECRET_TEMPLATES.md |
| GitHub integration | Connect repos, sync secrets, scan, webhooks | docs/GITHUB_INTEGRATION.md |
| Feature | Summary | Details |
|---|---|---|
| 2FA | TOTP, WebAuthn, backup codes | docs/2FA.md |
| IP allowlisting | CIDR / per-IP restrictions on projects | docs/IP_ALLOWLIST.md |
| Access requests | Time-limited elevated access with approval | docs/ACCESS_REQUESTS.md |
| Break-glass | Emergency access with multi-approver workflow | docs/BREAK_GLASS.md |
| Secret analytics | Weak/duplicate secrets, security scoring | docs/SECRET_ANALYTICS.md |
| Feature | Summary | Details |
|---|---|---|
| Secret rotation | Schedules and provider hooks (AWS, Stripe, custom) | docs/SECRET_ROTATION.md |
| Environment sync | Promote staging → production with approvals | docs/ENV_SYNC.md |
| CI/CD helpers | GitHub Actions, GitLab, CircleCI workflow snippets | docs/CICD_INTEGRATION.md |
| Notifications | Slack, Discord, generic webhooks | docs/NOTIFICATIONS.md |
| API keys & webhooks | REST API, outbound webhooks, SDK notes | docs/API_WEBHOOKS.md |
| Feature | Summary | Details |
|---|---|---|
| Team workspaces | Multi-project orgs, SAML/OIDC SSO | docs/TEAM_WORKSPACES.md |
| Self-hosted | Docker, Kubernetes, cloud runbooks | docs/SELF_HOSTED.md |
| Package | Summary | Details |
|---|---|---|
| CLI | Login, list projects/envs, push/pull .env, init |
CLI · docs/CLI.md |
| Browser extension | Fill env vars on Vercel, Netlify, GitHub, Railway, etc. | Browser extension · docs/BROWSER_EXTENSION.md |
Full feature index: docs/FEATURES_SUMMARY.md. Production setup checklist: docs/MANUAL_STEPS.md.
- Open
/quick-share. - Paste
.envcontent (or upload a file). - Choose TTL (1h–7d) and optional one-time or passphrase.
- Share the link; the decryption key stays in the
#fragment(never sent to the server). - Recipients open
/r/<token>#…to decrypt in the browser. - To revoke before expiry: use the delete token from the upload response with
DELETE /api/vault/:tokenand headerX-Delete-Token.
- Register at
/registerand sign in. - Dashboard → create a project (name + slug).
- Open the project → Environments tab → add a label (e.g.
staging,production) and paste.envcontent. - View, edit, or delete env files; share a stored env via quick-share from the project UI.
- Tabs on the project page: Version history, Audit log, GitHub (when configured).
- Project owner → invite by email with role editor or viewer.
- Invitee signs in with that email and opens
/invite/<token>. - Editors can create/update/delete env blobs; viewers can read and quick-share from stored envs.
Apply migrations through 0003_* (or latest in drizzle/) for invites, audit, GitHub, and enterprise tables—see docs/MANUAL_STEPS.md.
Releases are automatic when you merge to main (with changes under packages/cli or packages/browser-extension):
- CI ensures tag
v{version}exists (versionfrompackages/cli/package.json). - Builds CLI + extension and publishes assets to Releases (not just the Tags tab — open Releases for downloads).
| Secret | Required for | How to get it |
|---|---|---|
| (none) | GitHub Release assets only | Built-in GITHUB_TOKEN |
NPM_TOKEN |
npx @lucerowb/dot-vault on npm |
See npm token setup below |
- npmjs.com → Profile → Access Tokens → Generate New Token → Granular Access Token
- Permissions: Read and write for packages (scope
@lucerowbor all packages) - Enable “Bypass two-factor authentication (2FA) for automation” (required for CI; npm returns 403 without it)
- Copy the token value (
npm_…— shown only once at creation) into GitHub Settings → Secrets → Actions as secret nameNPM_TOKEN.
The token label on npm (e.g.dot-vault) is not the secret; you must paste thenpm_string. Re-save the secret after rotating the token.
Classic Automation tokens also work. A normal publish token without bypass 2FA will fail in GitHub Actions with 403 Forbidden.
To ship a new release: merge CLI or extension changes to main under packages/cli/** or packages/browser-extension/**. The Release workflow will:
- Detect what changed (CLI vs extension)
- Auto-bump the CLI patch version if that version is already on npm or already tagged
- Commit the version bump to
mainwith[skip ci] - Publish GitHub Release assets; publish npm (
@lucerowb/dot-vault) and update Homebrew only whenpackages/cli/**changed
You only need to edit packages/cli/package.json version manually for minor/major releases; patch releases are automatic after code changes.
Homebrew (macOS, project tap):
DotVault is distributed via a project tap, not homebrew/core (notability policy).
brew tap lucerowb/dot-vault https://github.com/lucerowb/dot-vault
brew install dot-vault
dv login --api-url https://your-dotvault.example.comUpgrade later: brew update && brew upgrade dot-vault
npm: The unscoped name dot-vault is blocked by npm (too similar to dotvault). The package is published as @lucerowb/dot-vault:
# Run once (no global install)
npx @lucerowb/dot-vault@latest login --api-url https://your-dotvault.example.com
# Or install globally (`dv`, `dot-vault`, `dotvault`)
npm install -g @lucerowb/dot-vault
dv login --api-url https://your-dotvault.example.com
# Or run `dv` with no args for a persistent interactive sessionFull CLI docs: packages/cli/README.md (shown on npm).
Fix a tag that has no assets (e.g. only “Source code zip”): Actions → Release → Run workflow → leave tag empty (uses package version) or set v0.1.0.
Each release includes:
| Asset | Install / use |
|---|---|
dotvault-cli-<version>.npm.tgz |
npm install -g ./dotvault-cli-0.1.0.npm.tgz (Node 18+) or Homebrew formula source |
dotvault-cli-<version>.tar.gz |
Extract, then node bin/dotvault.js (includes dependencies) |
dotvault-extension-<version>.zip |
Chrome → Extensions → Load unpacked (extract zip, select folder) |
SHA256SUMS-<version>.txt |
Checksums |
Downloads: github.com/lucerowb/dot-vault/releases
From the repo (development):
pnpm build:cli
pnpm dv login --api-url http://localhost:3000
pnpm dv # interactive session (project + env workflows)
pnpm dv pl production -p my-slug -o .env
pnpm dv ps .env.local -p my-slug -l staging
pnpm dv project-create "My App"Commands: login, logout, status, projects (ls), envs (e), pull (pl), push (ps), delete, init, project-create, shell, completion, help. See packages/cli/README.md and docs/CLI.md.
Default API URL is resolved at runtime (first match wins):
~/.dotvault/config.json(apiUrlfromdot-vault login --api-url)DOTVAULT_API_URLBETTER_AUTH_URLNEXT_PUBLIC_APP_URLhttp://localhost:3000
From the repo root, the CLI also loads .env.local / .env when you run it (same vars as the Next.js app). See docs/CLI.md.
pnpm build:extension- Chrome →
chrome://extensions→ Developer mode → Load unpacked → selectpackages/browser-extension/dist. - Open the popup → set your server URL (pre-filled from
DOTVAULT_API_URL/NEXT_PUBLIC_APP_URLat build time when present) → sign in. - On supported sites (Vercel, Netlify, GitHub Actions secrets, Railway, etc.), use the extension to fill variables from a selected project/env.
See docs/BROWSER_EXTENSION.md for platform URLs and permissions.
Authenticated session or API key (when enabled):
- Import:
POST /api/projects/:projectId/import— body with format and payload (docs/IMPORT_EXPORT.md). - Export:
GET /api/projects/:projectId/envs/:envId/export?format=env|json|….
- Set
GITHUB_APP_*/ webhook secrets in.env.local(see.env.example). - In the project dashboard → GitHub tab → connect repository.
- Configure sync rules; optional secret scanning via
/api/github/scan.
- Session cookie: same-origin requests from the web app.
- API keys and webhooks: docs/API_WEBHOOKS.md.
- CI/CD: generate workflow snippets from docs/CICD_INTEGRATION.md or use the CLI in GitHub Actions.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes (cloud vault) | Postgres URI (?sslmode=require in prod) |
BETTER_AUTH_SECRET |
Yes (prod) | Session signing (openssl rand -base64 32) |
BETTER_AUTH_URL |
Recommended | App origin for auth callbacks |
STORAGE_ENCRYPTION_KEY |
Yes (cloud vault) | Base64 32-byte key for at-rest env encryption |
NEXT_PUBLIC_APP_URL |
Recommended | Public URL for links and auth client |
UPSTASH_REDIS_REST_URL |
Quick share / rate limits | Upstash REST URL |
UPSTASH_REDIS_REST_TOKEN |
Quick share / rate limits | Upstash REST token |
Optional: SMTP (invites, resets), OAuth providers, GitHub App, S3 backups, workspace SSO—see .env.example and docs/MANUAL_STEPS.md.
Cloud-stored env plaintext is visible to the Next.js server when you view or save a file: the app decrypts with STORAGE_ENCRYPTION_KEY to serve the UI. This is not the same zero-knowledge model as quick share. Protect STORAGE_ENCRYPTION_KEY and your database like any secrets backend.
| Method | Path | Description |
|---|---|---|
POST |
/api/vault |
Store ciphertext in Redis (ttl, oneTime, iv, ciphertext) |
GET |
/api/vault/:token |
Fetch ciphertext metadata |
DELETE |
/api/vault/:token |
Revoke with X-Delete-Token |
| Method | Path | Description |
|---|---|---|
GET / POST |
/api/projects |
List / create projects |
GET / PATCH / DELETE |
/api/projects/:id |
Project CRUD |
GET / POST |
/api/projects/:id/envs |
List / create-or-overwrite by label |
GET / PATCH / DELETE |
/api/projects/:id/envs/:envId |
Env CRUD (decrypted content on GET) |
GET |
/api/projects/:id/envs/:envId/versions |
Version history |
POST |
/api/projects/:id/envs/:envId/versions |
Rollback |
GET |
/api/projects/:id/audit |
Audit log |
POST |
/api/projects/:id/import |
Bulk import |
GET |
/api/projects/:id/envs/:envId/export |
Export |
POST |
/api/projects/:id/invitations |
Invite collaborator |
POST |
/api/projects/invitations/accept |
Accept invite |
* |
/api/github/* |
GitHub connect, webhooks, scan |
Auth routes: /api/auth/* (Better Auth).
GitHub Actions (.github/workflows/ci.yml) uses path filters:
- App — lint, unit tests, Next.js build when
src/,drizzle/, etc. change - CLI —
pnpm --filter @lucerowb/dot-vault buildwhenpackages/cli/**changes - Extension —
pnpm --filter dotvault-browser-extension buildwhenpackages/browser-extension/**changes
Use Actions → CI → Run workflow to run all jobs manually.
Browse all guides in the app at /docs/ (powered by Docusaurus in packages/docs-site). Source markdown lives in docs/.
| Doc | Topic |
|---|---|
| FEATURES_SUMMARY.md | Feature index |
| MANUAL_STEPS.md | Production setup |
| CLI.md | CLI reference |
| BROWSER_EXTENSION.md | Extension |
| IMPORT_EXPORT.md | Import/export formats |
| VERSION_HISTORY.md | Versions and rollback |
| AUDIT_LOGS.md | Audit trail |
| GITHUB_INTEGRATION.md | GitHub App |
| 2FA.md | Two-factor auth |
| API_WEBHOOKS.md | API keys and webhooks |
| SELF_HOSTED.md | Docker / K8s deploy |
- Quick share: ciphertext on Redis; key in URL fragment; optional passphrase (
v2.fragments). - Cloud vault: server can decrypt with
STORAGE_ENCRYPTION_KEY; protect infra and logs. - Fragments may appear in browser history—use private windows when it matters.
This project does not provide legal or compliance advice.
- Postgres: apply all migrations in
drizzle/(orpnpm run db:migrate). - Set
DATABASE_URL,BETTER_AUTH_*,STORAGE_ENCRYPTION_KEY,NEXT_PUBLIC_APP_URL. - Optional: Upstash for quick share; SMTP for email; GitHub App vars.
- Deploy on Vercel, Coolify (
Dockerfile), or self-hosted.
Docker
- Production (app + baked-in docs at
/docs/):docker compose up --build - Development (live reload for app and docs):
docker compose -f docker-compose.dev.yml up --build→ http://localhost:3000 and http://localhost:3000/docs/
Ephemeral vault routes (/api/vault/*) use the Edge runtime; Better Auth + Postgres use Node.
Built with Next.js, Tailwind CSS, Framer Motion, Better Auth, Drizzle ORM, Supabase Postgres, Zod, and Upstash.
Issues and pull requests are welcome. See CONTRIBUTING.md and SECURITY.md for vulnerability reports.
If DotVault helps you, you can support development on Buy Me a Coffee. Sponsorship is optional; the software stays MIT-licensed either way.