Tengen
Self-Hostable password manager. Encrypted at rest, runs on your machine, never leaves your network
Install / Use
/learn @smadabat1/TengenREADME
Tengen
"I have been maintaining barriers for over 1000 years. Your secrets deserve the same."
A self-hosted, end-to-end encrypted private vault.
Features · Quick Start · Architecture · API · Roadmap · Changelog
</div>What is Tengen?
Tengen Gojo — wait, wrong show. Tengen, the immortal barrier master of Jujutsu Kaisen, has been maintaining impenetrable barriers for over a thousand years. We thought that was a solid metaphor for a private vault.
Tengen is an open-source, self-hosted private vault. No clouds. No telemetry. No "we take your privacy seriously" emails after a breach. Just your passwords, your machine, and a barrier that's been holding for a millennium.
All vault entries are encrypted with AES-256-GCM before they touch the database — the server never sees a plaintext password. Your encryption key is derived from your master password, lives only in a short-lived in-memory session cache, and is purged on logout or inactivity. Like Tengen himself, it leaves no trace.
⚠️ Unlike Tengen, your master password is not immortal. If you forget it, your vault is gone. No recovery. No reset. Write it down somewhere safe (ironic, we know). This also means all your encrypted notes are permanently lost.
Features
| Category | What's included |
|---|---|
| Passwords | Create, read, update, delete password entries · username, password, URL, notes, tags |
| Notes | Private encrypted notes · folders with contextual menus · tags · expandable block editor · per-note PIN/password lock |
| Encryption | AES-256-GCM per entry · fresh random 96-bit IV per write · ciphertext never leaves server |
| Key derivation | Argon2id (raw mode) for both authentication hash and AES-256 encryption key derivation — memory-hard, GPU-resistant |
| Breach detection | HaveIBeenPwned k-anonymity check · only SHA-1 prefix sent, never the full password · auto-checked on create/update · manual on-demand · batch scan all entries |
| Password health | zxcvbn strength scoring · vault-wide health dashboard · score history with area chart · tracks weak / pwned / reused / old passwords |
| Password generator | Cryptographically random · configurable length, charset, symbols |
| Search & filter | Inline search · tag filter · sort by date added / last updated / title |
| Command palette | Cmd/Ctrl+K global search · open entry, jump to tag, navigate pages |
| Session security | Auto-lock on inactivity · logout clears in-memory key · session tokens in sessionStorage only |
| Themes | Light / Dark / System — persisted per user |
| Self-hosted | Single docker-compose up · no telemetry · no external dependencies except HIBP |
Preview
<p align="center"> <img src="docs/images/login.png" alt="Tengen Login" width="800" /> </p> <p align="center"> <img src="docs/images/vault-cards.png" alt="Vault Cards view" width="48%" /> <img src="docs/images/vault-table.png" alt="Vault Table view" width="48%" /> </p> <p align="center"> <img src="docs/images/analyse.png" alt="Vault Analyse" width="48%" /> <img src="docs/images/health.png" alt="Vault health" width="48%" /> </p> <p align="center"> <img src="docs/images/password-generator.png" alt="Password generator" width="48%" /> <img src="docs/images/settings.png" alt="Vault settings" width="48%" /> </p>Quick Start
Tengen spent 1000 years setting up his barrier. You get 2 minutes.
Prerequisites
- Docker + Docker Compose v2
1 — Clone & configure
git clone https://github.com/your-username/tengen.git
cd tengen
cp .env.example .env # edit SECRET_KEY before going to production
2 — Start
docker-compose up --build -d
| Service | URL | |---|---| | Frontend | http://localhost:3000 | | Backend API | http://localhost:3000/api |
3 — Stop
docker-compose down
Data persistence — the SQLite database is stored in
./data/tengen.db(mounted as a volume). It survives container restarts. Back it up like your life depends on it — because your passwords do.
Configuration
All configuration is done via environment variables in .env.
| Variable | Default | Description |
|---|---|---|
| SECRET_KEY | (required) | JWT signing secret — change this before deploying, "secret" is not a secret |
| ACCESS_TOKEN_EXPIRE_MINUTES | 60 | JWT TTL |
| ARGON2_TIME_COST | 3 | Argon2id iterations |
| ARGON2_MEMORY_COST | 65536 | Argon2id memory (KB) |
| ARGON2_PARALLELISM | 2 | Argon2id parallelism |
| DATA_DIR | /app/data | SQLite database directory |
| LOG_DIR | /app/logs | Log file directory |
| DEBUG | false | Enable debug logging — keep this off in production unless you enjoy pain |
Architecture
Tengen's barrier technique works in layers. So does ours.
┌─────────────────────────────────────────────────────┐
│ Browser │
│ │
│ React 18 + Vite │
│ TanStack Router · React Query · Zustand │
│ Tailwind CSS · Framer Motion · Radix UI │
└──────────────────────┬──────────────────────────────┘
│ HTTP (Nginx reverse proxy)
┌──────────────────────▼──────────────────────────────┐
│ FastAPI (Uvicorn) │
│ │
│ auth/ vault/ tools/ core/ │
│ ├─ JWT + Argon2id auth │
│ ├─ AES-256-GCM encryption service (passwords │
│ │ & notes) │
│ ├─ Per-note lock cache (notes_unlock_cache) │
│ ├─ HIBP k-anonymity client (httpx async) │
│ ├─ zxcvbn password strength │
│ └─ In-memory session key cache (the barrier) │
│ │
│ SQLAlchemy ORM → SQLite (WAL mode) │
│ models: User · VaultEntry · Note · NoteFolder │
└─────────────────────────────────────────────────────┘
Security model
- At rest — every vault entry (passwords, usernames, URLs, field notes) and every private note's body are AES-256-GCM encrypted individually. The encryption key is never written to disk. Ever.
- Authentication — master passwords are hashed with Argon2id, the current gold standard for password hashing. Not bcrypt. Not MD5. Please not MD5.
- Key derivation & lifecycle — Argon2id raw mode derives the 256-bit AES key from master password + stored salt on login (same algorithm used for the auth hash, same tunable env params). The key is never stored — it lives only in a TTL session cache and is purged on logout or expiry.
- Per-note locks — individual notes can be protected with an additional PIN/password on top of the vault master key. Unlocked notes are held in a short-lived in-memory cache separate from the main session cache and are cleared on lock or logout.
- HIBP privacy — only the first 5 hex characters of
SHA1(password)are sent to HaveIBeenPwned. The full hash and plaintext never leave your machine. This is called k-anonymity and it's clever.
Project Structure
tengen/
├── backend/
│ ├── auth/ # Registration, login, JWT
│ ├── vault/ # Password entries + Notes CRUD + AES-256-GCM encryption
│ │ ├── service.py # Password entry service
│ │ ├── notes_service.py # Notes & folders service
│ │ ├── encryption.py # AES-256-GCM helpers
│ │ ├── notes_unlock_cache.py # Per-note lock session cache
│ │ └── router.py # Vault + Notes API routes
│ ├── tools/ # Generator, strength, HIBP, health
│ ├── core/ # Config, security, logger, session cache
│ ├── models.py # SQLAlchemy ORM models (User, VaultEntry, Note, NoteFolder)
│ ├── schemas.py # Pydantic request/response schemas
│ ├── database.py # DB session factory + WAL setup
│ ├── main.py # FastAPI app factory + lifespan
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── api/ # Axios client + API modules
│ │ ├── components/ # Layout, vault, notes, UI primitives
│ │ ├── pages/ # Vault, Notes, Health, Analyse, Generator, Settings
│ │ ├── store/ # Zustand auth store
│ │ ├── hooks/ # useAutoLock, useClipboard
│ │ └── router.jsx # TanStack Router route t
