04/26 Update Claude.md, Readme.md

This commit is contained in:
2026-04-26 11:11:18 -04:00
parent f37d3e5c9a
commit 86888d7b25
2 changed files with 348 additions and 584 deletions
+117 -129
View File
@@ -1,38 +1,50 @@
# PassKeeper 🔐
A self-hosted, zero-knowledge password manager — web app and browser extension. Modelled after LastPass, built on Flask + MySQL + Web Crypto API.
Your master password and decrypted vault data **never leave your browser**. The server stores only encrypted blobs it cannot read.
A self-hosted, zero-knowledge password manager — web app and Chrome/Firefox extension. Your master password and decrypted vault data **never leave your browser**.
---
## Features
### Web App
- **Zero-knowledge encryption** — AES-256-GCM client-side, 600k-iteration PBKDF2 vault key derivation
- **7 item types** — Passwords, Secure Notes, Payment Cards, Bank Accounts, Addresses, Identities, Passkeys
- **TOTP/2FA support** — per-site TOTP codes stored inside the encrypted vault blob; live 6-digit display with countdown
- **Folder organisation** — create, rename, delete folders; filter vault by folder
- **Item sharing** — zero-knowledge ECDH P-256 re-encryption; share with any registered user
- **Zero-knowledge encryption** — AES-256-GCM client-side; 600k-iteration PBKDF2 vault key
- **7 item types** — Passwords, Secure Notes, Cards, Bank Accounts, Addresses, Identities, Passkeys
- **Vault item tags** — comma-separated tags stored inside the encrypted blob; sidebar tag filter; live badge preview in modal; no schema change required
- **Collapsible folder groups** — click any folder header in the vault list to collapse/expand; item count badge and chevron indicator; state persists across re-renders
- **TOTP / 2FA** — per-site TOTP codes stored in `plain.totp_uri`; live 6-digit display with countdown
- **Folder organisation** — create, rename, delete; filter vault by folder
- **Item sharing** — ECDH P-256 zero-knowledge re-encryption; share with any registered user
- **Emergency access** — configurable wait-timer access grant for a trusted contact
- **Security dashboard** — weak, reused, and old password detection + **HaveIBeenPwned k-anonymity breach check** (passwords never transmitted)
- **Account MFA** — TOTP-based login verification (Google Authenticator / Authy)
- **Master password change** — atomic zero-knowledge re-encryption of the entire vault
- **Security dashboard** — weak / reused / old / **no 2FA saved** / **HaveIBeenPwned breach check** (k-anonymity — passwords never transmitted)
- **Import / Export** — encrypted JSON backup; CSV export (plaintext, handle carefully); import from Chrome, Bitwarden, and 1Password CSV formats
- **Account MFA** — TOTP-based login (Google Authenticator / Authy)
- **Master password change** — atomic zero-knowledge re-encryption of entire vault including item names
- **Account recovery** — 128-bit recovery code; server never stores it
- **Audit log** — server-side trail of all create/edit/delete actions; no sensitive data ever logged
- **Encrypted item names** — item names stored as AES-256-GCM ciphertext; server holds only the item type as a label
- **Responsive layout** — phone, tablet, laptop, and large desktop breakpoints; collapsible sidebar
- **Audit log** — server-side trail of all create/edit/delete/import/export actions
- **Encrypted item names** — `enc_name`/`iv_name`; server holds only the item type as a label
- **Browser history** — back/forward button works for all views (`history.pushState`)
- **Web-app auto-lock** — configurable inactivity timeout (5/10/15/30/60 min or Never); stored per browser in `localStorage`
- **Clipboard auto-clear** — sensitive copies cleared after 30 seconds
- **Responsive layout** — phone, tablet, laptop, large desktop; collapsible sidebar
### Browser Extension (Chrome / Edge — Manifest V3)
- **Autofill** — detects login forms; injects icon into username and password fields only (scored heuristic, not all text inputs)
- **Suggestion dropdown** — anchored below the focused field; filters as you type; keyboard navigation (↑↓ Enter); fills with one click
- **Smart domain matching** — matches vault items by domain name, handles bare domains (`github.com`) and subdomains
- **Auto-save banner** — prompts to save or update credentials on form submit; duplicate detection; "Never for this site" blocklist
- **Inline password generator** — length slider, charset toggles, strength indicator; fully CSPRNG (`crypto.getRandomValues` throughout)
- **TOTP live display** — 6-digit code + countdown timer per item in the popup
- **SSO bridge** — log in once on the web app; extension picks up the session automatically
- **Idle lock** — configurable auto-lock timeout (1 / 5 / 10 / 30 min / Never)
- **Security** — decrypted vault data stored in `chrome.storage.session` only (memory-only, never written to disk)
### Browser Extension (Chrome / Edge — Manifest V3 + Firefox — Manifest V2)
- **Autofill** — detects login forms; injects icon into username and password fields only (scored heuristic, not all inputs)
- **Smart domain matching** — matches by domain name; handles bare domains (`github.com`) and subdomains
- **Suggestion dropdown** — filters as you type; keyboard navigation (↑↓ Enter); one-click fill
- **Collapsible folder groups** — "All items" tab groups by folder with collapse/expand toggle matching the web app UX
- **Favorites tab** — items tagged `favorite` in the web app appear in a dedicated tab; marked with ★
- **Tag display** — purple badge pills on item rows; tags sourced from `plain.tags[]`
- **Three-dot flyout menu** — contextual actions: Open URL / Copy username / Copy password; built dynamically from what the item has
- **Dedicated copy-username button** — person icon alongside copy-password in every item row
- **Auto-save banner** — save/update credentials on form submit; duplicate detection; "Never for this site" blocklist
- **Pending-save badge** — red `!` on toolbar icon when a credential is waiting to be saved
- **Inline password generator** — fully CSPRNG (`crypto.getRandomValues` throughout, `Math.random` never called)
- **TOTP live display** — 6-digit code + countdown per item
- **SSO bridge** — log in once on the web app; extension picks up the session
- **Idle lock** — configurable auto-lock (1/5/10/30 min or Never)
- **Clipboard auto-clear** — passwords/usernames cleared from clipboard after 30 seconds
- **Keyboard shortcut** — `Ctrl+Shift+L` / `Cmd+Shift+L` to open popup (customisable at `chrome://extensions/shortcuts`)
- **Firefox compatible** — separate MV2 manifest + background script; same popup/content/bridge code
---
@@ -42,18 +54,19 @@ Your master password and decrypted vault data **never leave your browser**. The
Master Password
├─ PBKDF2(email, 100k iter) ──► authHash ──► POST /api/auth/login
│ Argon2id(authHash) stored in DB
Argon2id(authHash) stored in DB
└─ PBKDF2(enc_key_salt, 600k iter) ──► vaultKey (stays in browser memory only)
AES-256-GCM encrypt
enc_data + iv ──► POST /api/vault
enc_name + iv_name (item name, encrypted separately)
└─ PBKDF2(enc_key_salt, 600k iter) ──► vaultKey (browser memory only)
AES-256-GCM encrypt
enc_data + iv (item payload + tags)
enc_name + iv_name (item name)
POST /api/vault ──► Server stores ciphertext only
```
The server is blind to all vault content. A database breach exposes only encrypted ciphertext.
A database breach exposes only encrypted ciphertext. The server cannot read vault names, passwords, or tags.
---
@@ -66,7 +79,7 @@ The server is blind to all vault content. A database breach exposes only encrypt
| Frontend | Vanilla JS, Web Crypto API, Jinja2 |
| Auth | Argon2id + PBKDF2 + JWT (HS256) |
| Encryption | AES-256-GCM (client-side) |
| Extension | Chrome Manifest V3 |
| Extension | Chrome MV3 / Firefox MV2 |
| Web server | Nginx + Gunicorn + systemd |
| Rate limiting | Flask-Limiter + Redis |
| TLS | Let's Encrypt / Certbot |
@@ -76,136 +89,104 @@ The server is blind to all vault content. A database breach exposes only encrypt
## Getting Started
### Prerequisites
- Python 3.12+
- MySQL 8.x
- Node.js is **not** required — no build step
- No Node.js or build step required
### Development (Windows)
```bash
# 1. Clone and set up virtual environment
git clone https://github.com/yourname/passkeeper
cd passkeeper
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
# 2. Configure environment
copy .env.example .env
```
Edit `.env`:
```env
FLASK_ENV=development
SECRET_KEY=your-secret-key
JWT_SECRET_KEY=your-jwt-secret
SECRET_KEY=<64-char hex>
JWT_SECRET_KEY=<64-char hex>
MYSQL_HOST=localhost
MYSQL_USER=passkeeper
MYSQL_PASSWORD=yourpassword
MYSQL_DB=passkeeper
TOTP_ENCRYPTION_KEY=64-char-hex-string # python -c "import secrets; print(secrets.token_hex(32))"
TOTP_ENCRYPTION_KEY=<64-char hex>
```
```bash
# 3. Create database
mysql -u root -p -e "CREATE DATABASE passkeeper CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -p -e "CREATE USER 'passkeeper'@'localhost' IDENTIFIED BY 'yourpassword';"
mysql -u root -p -e "GRANT ALL PRIVILEGES ON passkeeper.* TO 'passkeeper'@'localhost'; FLUSH PRIVILEGES;"
# 4. Create tables
mysql -u root -p -e "CREATE USER 'passkeeper'@'localhost' IDENTIFIED BY 'yourpassword'; GRANT ALL ON passkeeper.* TO 'passkeeper'@'localhost'; FLUSH PRIVILEGES;"
python reset_db.py
# 5. Run
python run.py
```
Open `http://localhost:5000`.
Generate secrets:
```bash
python -c "import secrets; print(secrets.token_hex(32))"
```
### Load the Extension (Chrome)
1. `chrome://extensions/` → Enable **Developer mode**
2. **Load unpacked** → select `extension/` folder
3. Reload after any JS/CSS changes
1. Open `chrome://extensions/`
2. Enable **Developer mode** (top-right toggle)
3. Click **Load unpacked** → select the `extension/` folder
4. Reload the extension after any JS/CSS changes
Content script logs appear in the **page's** DevTools console. Background service worker logs are at `chrome://extensions/` → PassKeeper → "Service Worker".
### Load the Extension (Firefox)
1. `about:debugging`**This Firefox****Load Temporary Add-on**
2. Select `extension/manifest.firefox.json`
---
## Production Deployment
### 1. Server dependencies
```bash
sudo apt update && sudo apt install python3.12 python3.12-venv mysql-server nginx \
certbot python3-certbot-nginx redis-server
```
### 2. Application setup
```bash
cd /var/www/passkeeper
python3.12 -m venv .venv
source .venv/bin/activate
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # set production values
flask db upgrade # run all migrations
```
### 3. systemd service
Copy `scripts/passkeeper.service` to `/etc/systemd/system/passkeeper.service`, then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now passkeeper
journalctl -xeu passkeeper.service
sudo cp scripts/passkeeper.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now passkeeper
```
Key flags in the service unit: `--preload` (single app import, lower memory), `WatchdogSec=60s`, `Restart=on-failure`.
### 4. Nginx
Copy `scripts/passkeeper-nginx.conf` to `/etc/nginx/sites-available/passkeeper`, update `server_name`, then:
```bash
sudo cp scripts/passkeeper-nginx.conf /etc/nginx/sites-available/passkeeper
sudo ln -s /etc/nginx/sites-available/passkeeper /etc/nginx/sites-enabled/
sudo certbot --nginx -d yourdomain.com
sudo nginx -t && sudo systemctl reload nginx
```
The Nginx config uses an `upstream` block with `proxy_next_upstream` for zero-downtime Gunicorn restarts, dual rate-limit zones (auth endpoints and general API), and `Cache-Control: immutable` for static assets.
### 5. Schema migrations (future updates)
### 5. Future schema migrations
```bash
flask db upgrade
sudo systemctl reload passkeeper
flask db upgrade && sudo systemctl reload passkeeper
```
---
## Environment Variables
| Variable | Description | Example |
|---|---|---|
| `SECRET_KEY` | Flask session secret | 64-char random hex |
| `JWT_SECRET_KEY` | JWT signing secret | 64-char random hex |
| `MYSQL_HOST` | MySQL host | `localhost` |
| `MYSQL_USER` | MySQL username | `passkeeper` |
| `MYSQL_PASSWORD` | MySQL password | — |
| `MYSQL_DB` | Database name | `passkeeper` |
| `TOTP_ENCRYPTION_KEY` | Server-side AES key for TOTP secrets | 64-char hex |
| `RATELIMIT_STORAGE_URI` | Redis URI for rate limiting | `redis://127.0.0.1:6379/0` |
| `CORS_ORIGINS` | Allowed CORS origins | `https://yourdomain.com` |
Generate secrets:
```bash
python -c "import secrets; print(secrets.token_hex(32))"
```
| Variable | Description |
|---|---|
| `SECRET_KEY` | Flask session secret (64-char hex) |
| `JWT_SECRET_KEY` | JWT signing secret (64-char hex) |
| `MYSQL_HOST` / `MYSQL_USER` / `MYSQL_PASSWORD` / `MYSQL_DB` | Database |
| `TOTP_ENCRYPTION_KEY` | Server-side AES key for TOTP secrets (64-char hex) |
| `RATELIMIT_STORAGE_URI` | Redis URI — required in production (`redis://127.0.0.1:6379/0`) |
| `CORS_ORIGINS` | Allowed origins (`*` in dev, domain in prod) |
---
@@ -213,64 +194,71 @@ python -c "import secrets; print(secrets.token_hex(32))"
```
passkeeper/
├── app/ # Flask application
│ ├── models/ # SQLAlchemy models
│ ├── routes/ # API blueprints (auth, vault, folders, sharing, emergency)
│ ├── services/ # Auth service (Argon2id, JWT, TOTP encryption)
│ ├── static/js/ # Client-side crypto + vault UI
│ └── templates/ # Jinja2 HTML templates
├── extension/ # Chrome extension (Manifest V3)
│ ├── popup/ # Popup UI (HTML + CSS + JS)
│ ├── content/ # Content script (form detection, autofill icon, dropdown)
│ ├── shared/ # Shared crypto (vault key, encryptName/decryptName)
│ ├── bridge/ # SSO bridge (web app ↔ extension session sync)
── background.js # Service worker (badges, message relay, idle lock)
├── migrations/ # Alembic migration scripts
├── scripts/ # Nginx config, systemd unit, backup scripts
├── reset_db.py # Dev-only: drop + recreate all tables
├── requirements.txt
├── run.py # Development server entry point
└── wsgi.py # Gunicorn entry point
├── app/ # Flask application
│ ├── models/ # SQLAlchemy models
│ ├── routes/ # API blueprints (auth, vault, folders, sharing, emergency)
│ ├── services/ # Auth (Argon2id, JWT, TOTP encryption)
│ ├── static/js/ # Client-side crypto + vault UI
│ └── templates/ # Jinja2 templates
├── extension/ # Browser extension
│ ├── popup/ # Popup UI
│ ├── content/ # Content script (autofill, field detection)
│ ├── shared/ # Shared crypto + Firefox polyfill
│ ├── bridge/ # SSO bridge
── background.js # Chrome MV3 service worker
│ ├── background.firefox.js # Firefox MV2 background page
│ ├── manifest.json # Chrome/Edge MV3
│ └── manifest.firefox.json # Firefox MV2
├── migrations/ # Alembic migration scripts
├── scripts/ # Nginx, systemd, backup
└── requirements.txt
```
---
## API Overview
All vault and folder endpoints require `Authorization: Bearer <access_token>`.
All vault/folder endpoints require `Authorization: Bearer <access_token>`.
| Method | Endpoint | Description |
|---|---|---|
| POST | `/api/auth/register` | Create account |
| POST | `/api/auth/login` | Authenticate; returns tokens or MFA challenge |
| POST | `/api/auth/mfa/verify` | Complete MFA step |
| POST | `/api/auth/refresh` | Rotate refresh token |
| POST | `/api/auth/logout` | Blacklist both tokens |
| GET | `/api/vault` | List all encrypted vault items |
| POST | `/api/vault` | Create item (`enc_data`, `iv`, `enc_name`, `iv_name`) |
| POST | `/api/auth/login` | Authenticate |
| POST | `/api/auth/mfa/verify` | Complete MFA |
| POST | `/api/auth/refresh` | Rotate tokens |
| POST | `/api/auth/logout` | Blacklist tokens |
| GET | `/api/vault` | List encrypted items |
| POST | `/api/vault` | Create item (`enc_data`, `iv`, `enc_name`, `iv_name`, tags in payload) |
| PUT | `/api/vault/<id>` | Update item |
| DELETE | `/api/vault/<id>` | Delete item |
| GET | `/api/folders` | List folders |
| GET | `/api/vault/export` | Download encrypted JSON backup |
| POST | `/api/vault/import` | Bulk import; returns `{ imported, skipped }` |
| GET/POST | `/api/folders` | List / create folders |
| POST | `/api/sharing` | Share item (ECDH re-encryption) |
| POST | `/api/emergency` | Create emergency access grant |
| POST | `/api/auth/change-password` | Atomic vault re-encryption on password change |
| POST | `/api/auth/recover` | Account recovery (one-time use) |
| POST | `/api/auth/change-password` | Atomic vault re-encryption |
| POST | `/api/auth/recover` | Account recovery (one-time) |
---
## Item Tagging
Tags are stored as `plain.tags: string[]` inside the encrypted vault blob — the server never sees them and no schema change is required.
**Web app:** Add tags in the item edit modal (comma-separated). Tags appear as purple badge pills on item rows. A Tags section in the sidebar lets you filter by any tag.
**Extension:** Tags appear as `.pk-tag` badges on item rows. Items tagged `favorite` appear in the **Favorites** tab and show a ★ in the site label.
---
## Backup
Automated MySQL backups with 30-day retention:
```bash
# Install cron job
sudo cp scripts/passkeeper-logrotate /etc/logrotate.d/passkeeper
crontab scripts/backup.cron
```
Manual backup:
```bash
# Manual backup
bash scripts/backup_db.sh
```