A lightweight and secure two-factor authentication (2FA) token manager.
xVault is a self-hosted 2FA token manager that stores TOTP codes securely with AES encryption. It features a clean, minimalist interface and runs locally for maximum security.
- 🔐 Secure Storage — AES-encrypted vault with password protection
- ⚡ Auto-Generation — Time-based codes refresh every 30 seconds
- 📱 QR Code Scanner — Quickly add accounts by scanning QR codes
- 📦 Export/Import — Encrypted backup and restore functionality
- 🎨 Modern UI — Clean, responsive interface with dark mode support
- 🔒 Local First — Self-hosted for complete data control
- 📁 Organization — Organize entries with folders and custom icons
- Frontend: React 18 + TypeScript + Vite + Tailwind CSS
- Backend: Express.js + SQLite
- Encryption: AES (CryptoJS)
- Authentication: Session-based with login ID system
- Node.js v18 or higher
- npm v9 or higher (or compatible package manager)
git clone https://github.com/Kisakay/xVault.git
cd xVault# Install client dependencies
npm install
# Install server dependencies
cd server && npm install && cd ..Copy the example configuration file:
cp config.example.json config.jsonEdit config.json with your settings:
{
"SERVER_HOST": "localhost",
"SERVER_PORT": 3001,
"SERVER_URL": "http://localhost:3001"
}For production, update SERVER_URL to match your domain.
npm startThis starts:
- Client (Vite dev server) on
http://localhost:5173 - Server (Express API) on
http://localhost:3001
# Client only (development server)
npm run dev
# Server only (API server)
npm run servernpm run buildThe built files will be in the dist/ directory, which the server serves in production mode.
- Launch the application and open the web interface
- Create a new account with a strong password
- Save your Login ID — you'll need it to log in
- Add your first TOTP secret via QR code or manual entry
- Click the Add button
- Scan a QR code or enter the secret key manually
- Name the service and (optionally) add a custom icon
- Organize entries into folders for better management
- Save your entry
TOTP codes are automatically generated and refresh every 30 seconds. Click on any entry to copy the code to your clipboard.
Export:
- Navigate to Settings
- Select Export Vault
- Enter your password to confirm
- Save the encrypted JSON file securely
Import:
- Navigate to Settings
- Select Import Vault
- Choose your exported file
- Enter your password to decrypt and import
# Install PM2 globally
npm install -g pm2
# Build the application
npm run build
# Start with PM2 using ecosystem config
pm2 start ecosystem.config.js
# Save PM2 process list
pm2 save
# Setup PM2 to start on system boot
pm2 startupUsing Docker Compose (Recommended):
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose downUsing Docker directly:
# Build the image
docker build -t xvault .
# Run the container
docker run -d \
-p 3001:3001 \
-v xvault_data:/app/server \
--name xvault \
--restart unless-stopped \
xvaultThe server will be available at http://localhost:3001 (or your configured domain).
Create /etc/systemd/system/xvault.service:
[Unit]
Description=xVault 2FA Manager
After=network.target
[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/xVault
ExecStart=/usr/bin/node server/server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl enable xvault
sudo systemctl start xvault- Use HTTPS — Always use HTTPS in production (configure reverse proxy with Nginx + Let's Encrypt)
- Strong Password — Use a unique, complex password for your vault
- Local Access — Run locally or access via VPN; avoid exposing to the internet
- Regular Backups — Back up
server/xVault.sqlitefrequently - Keep Updated — Regularly update dependencies and the application
Example Nginx configuration with HTTPS:
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}The critical file is server/xVault.sqlite which contains all encrypted data:
# Backup the database
cp server/xVault.sqlite /path/to/backup/xVault-$(date +%Y%m%d).sqlite
# Restore from backup
cp /path/to/backup/xVault-YYYYMMDD.sqlite server/xVault.sqliteCreate a backup script (backup.sh):
#!/bin/bash
BACKUP_DIR="/path/to/backups"
DATE=$(date +%Y%m%d-%H%M%S)
cp server/xVault.sqlite "$BACKUP_DIR/xVault-$DATE.sqlite"
# Keep only last 30 days of backups
find "$BACKUP_DIR" -name "xVault-*.sqlite" -mtime +30 -deleteAdd to crontab for daily backups:
0 2 * * * /path/to/backup.shEdit config.json in the root directory:
{
"SERVER_HOST": "0.0.0.0",
"SERVER_PORT": 3001,
"SERVER_URL": "https://your-domain.com"
}- SERVER_HOST: Host to bind to (
0.0.0.0for all interfaces,localhostfor local only) - SERVER_PORT: Port the server listens on
- SERVER_URL: Public URL of your server (used for API calls)
The server can also be configured using environment variables:
export SERVER_HOST=0.0.0.0
export SERVER_PORT=3001
export SERVER_URL=https://your-domain.com
export NODE_ENV=productionIf port 3001 is already in use, either:
- Change the port in
config.json - Stop the process using the port:
lsof -ti:3001 | xargs kill
If you encounter database errors:
- Check file permissions on
server/xVault.sqlite - Ensure the
server/directory is writable - Verify SQLite3 is properly installed
If the build fails:
- Clear
node_modulesand reinstall:rm -rf node_modules && npm install - Clear Vite cache:
rm -rf node_modules/.vite - Check Node.js version:
node --version(should be v18+)
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
This project is licensed under the MIT License. See the LICENSE file for details.
For issues, questions, or contributions, please open an issue on GitHub.