End user guide
FortPass is unlocked with a PIN (4–8 digits). From that PIN, an AES-256 key is derived that encrypts accounts, cards, banks, notes, documents and TOTP secrets before they touch the database. Without the PIN, what's in the DB is noise — server, admin and support team cannot decrypt it.
Quick glossary
| Term | Meaning |
|---|---|
| PIN | 4–8 digits. Master key. Bcrypt-hashed in DB, never in plaintext. |
| vault_key | AES-256 key derived from the PIN with PBKDF2-SHA256 (200k iterations). Lives only in the PHP session while the user is authenticated. |
| Privileged PIN session | After the correct PIN, the privileged session expires after 15 min of inactivity and asks for the PIN again. |
| HIBP | Have I Been Pwned. Indexes leaked passwords. We use its k-anonymity API (only the first 5 chars of SHA-1 leave the server — the password never goes out in plain). |
| Zero-knowledge | The server can never decrypt the user's data at any point. |
| Wrapped encryptionKey | Each item has its own AES key. That key is wrapped with the vault_key before being stored in DB. |
Create an account and PIN
Three sign-up paths:
- Email passwordless: enter your email at
/auth, get a 6-digit code (valid 10 min, max 5 attempts, 3 codes/h per (email, IP)). - Google Sign In: if the admin enabled it.
- Apple Sign In: if the admin enabled it.
After the first login you are asked to create a PIN and shown a recovery code once. Save it somewhere safe: with it you can recover the PIN without losing any encrypted data. Without it, recovering the PIN requires wiping the entire vault.
From then on, every 15 minutes of inactivity prompts the PIN again. Five failed attempts per (IP + user) in 60s temporarily locks you out — anti-brute-force.
Accounts, cards, banks, notes, documents
Each section stores encrypted items:
/accounts· web accounts (title, icon, username, password, folder)./credit-cards· cards (holder, number, expiry, CVC)./banks· banks (bank, IBAN, BIC, alias)./notes· rich-text notes. Notes marked as secret are encrypted; regular ones are only stored (useful for shopping lists)./documents· encrypted files (PDFs, images, etc.). Total size capped by plan (max_storage_mb).
Common features:
- Folders with custom icon and color.
- Real-time search (client-side).
- Persistent sort (cookie) — Newest / Oldest / A-Z / Z-A. Also available in
/authenticator. - Password generator embedded in the accounts form.
- Auto icon fill (Brand Symbols Outlined, mapped by name).
Authenticator (TOTP/HOTP)
/authenticator generates 2FA codes Google Authenticator/Authy style. Two intake methods:
- Scan QR with the camera (jsQR, everything in-browser, the secret never leaves).
- Manual entering the base32 secret, issuer and optional period/digits/algorithm.
Codes rotate every 30s (TOTP) or are generated by counter (HOTP, refresh button). Click the code to copy.
Password generator
Available in two places:
- Embedded modal in the accounts form ("Generate" button). The result is injected into the password input on "Use".
- Standalone page at
/password-generator. Public access (no login). Anonymous visitors see a CTA towards/auth+ testimonials + FAQ.
Two modes:
- Random: 8–64 characters, configurable charsets (
A-Z,a-z,0-9, symbols), optional ambiguity exclusion (0/O,1/l/I, etc.). - Passphrase: 3–10 short words, configurable separators, capitalisation and trailing digit.
Cryptographic randomness (crypto.getRandomValues with rejection sampling — no modular bias). The entropy meter computes real bits (not zxcvbn-style heuristics).
Vault health
/vault-health scans every password owned by the user and classifies them as:
- Leaked · appear in a public breach (via HIBP k-anonymity).
- Reused · same password across ≥ 2 accounts.
- Weak · score ≤ 2 (short length, lack of charset variety, common words, sequences, repetitions).
Each scan is persisted in security_scans (last 20 per user, cron prunes the old ones). The history is visible only if the plan includes scan_history_visible.
The number of scans per month is capped by max_scans_per_month on the plan.
Emergency contacts
/account-and-security#emergency. Designate trusted people who can rescue your vault if you lose access (forgotten PIN, accident, death).
Flow:
- The user designates up to
max_emergency_contactscontacts. Each gets an invitation email to accept. - On accept, the contact gets a unique emergency code (zero-knowledge: only that person knows it, we never see it).
- If the contact needs to rescue access, they click the link they received and request access.
- A configurable waiting period starts (e.g. 7 days) during which the owner can deny the request from their panel.
- If not denied, after the wait ends the contact gets the encrypted recovery code, which they open with their emergency code.
If the plan allows 0 contacts, the tab shows an upsell panel. Defense in depth: the invite endpoint also rejects when max_emergency_contacts === 0.
Subscription & plan change
/billing· current plan, payment method, next charge, payment history. Buttons to change plan or cancel./billing/change-plan· grid of plans with summary card, monthly/annual/lifetime toggle./pricing· public page with the same plans (no login).
If the user has no active subscription or their plan was removed by the admin, the app treats them as Free automatically. /billing shows "Current plan: Free · Free" instead of an empty panel.
Email notifications
/account-and-security#pin, "Notifications" section. Three toggles:
- Login alerts · always available. Email when a new login is detected.
- Monthly security summary · requires
monthly_summary_emailin the plan. If the plan doesn't include it, the toggle is dimmed with a red "NOT INCLUDED" tag. - Breach alerts · requires
breach_alerts_basicorbreach_alerts_realtime. See Plans & features.
Defense: even if you turn on a toggle, the sender checks the plan at send time — an email the plan doesn't allow is never sent.
Multi-device & sessions
Each login creates a row in user_sessions with last_active_at, IP, UA. The plan caps max_devices simultaneously.
If you try to sign in while there are already ≥ max_devices live sessions, the app shows a "Too many devices" modal with the session list (browser, OS, IP, last active). You pick which to close to make room.
There's a "Select all / Deselect all" button to ease cleanup.