One Master Key. Every password, derived.
A deterministic, client-side password manager. No server, no vault, no sync. Your Master Key plus the site you're visiting always produce the same password — on any device, on any version, forever.
Most password managers are vaults: they sit between you and your accounts, sync your secrets through a cloud, and ask you to trust a vendor with the keys to your digital life. OfflinePass is the other model — there is no vault. The "manager" is a pure function:
password = base58(hmac-sha256(masterKey, "host|identity|year|n")).slice(0, 16)
Type your Master Key, the site, and your identity. Get a 16-character password. Same inputs always yield the same output, so there is nothing to back up except your Master Key — which lives only in your head.
| OfflinePass | Traditional vault | |
|---|---|---|
| Server | None | Required for sync |
| Vault to back up | None | Yes (often encrypted at rest) |
| Recover after device loss | Type your Master Key | Restore vault from backup |
| What an attacker steals if they breach you | Nothing — there's nothing on the wire | The whole vault |
| Offline-able | Always | Depends |
| Auditable in one sitting | Yes (~150 LOC of crypto) | No |
const msg = "github.com|you@example.com|2026|0"; // host | identity | year | rotation
const mac = hmacSha256(masterKey, msg); // RFC 2104
const password = base58(mac).slice(0, 16); // ~94 bits of entropy
// → 0$87booSaeaKYnhgEqThat's the entire algorithm. The leading 0$ is the rotation counter —
bump it when a site forces a password change, and the same Master Key
gives you a fresh password without any "vault update" anywhere.
The reference implementation lives in
web/src/utils/hmacUtils.ts and is
mirrored verbatim in the
Chrome extension and the
Flutter app. All three produce
the same output for the same inputs — see
SECURITY.md.
- Web: open https://offlinepass.com and use it. Open the network tab — there are no requests after the page loads.
- Chrome extension: install from the
Chrome Web Store,
open the popup with
Ctrl+Shift+F(MacCtrl+Shift+Fon macOS), type your Master Key. - Mobile: build the Flutter app from
app/. - Run locally: see Develop locally.
offlinepass/
├── web/ Next.js 16 + React 19 web app, deployed to GitHub Pages
├── chrome_extension/ Chrome MV3 popup, same Next.js stack
│ └── store-assets/ Chrome Web Store listing copy & art
├── app/ Flutter mobile/desktop app
├── CONTRIBUTING.md How to develop, test, and propose changes
├── SECURITY.md Threat model, crypto details, vulnerability reporting
└── LICENSE Apache 2.0
The three implementations are independent codebases that share an
algorithm. Each one passes the same fixture-based parity tests, so a
password generated on the web for github.com is byte-identical to the
one generated by the Chrome extension or the Flutter app for the same
inputs.
If you don't want to retype your Master Key every visit, OfflinePass can encrypt it locally with a PIN of your choice:
- KDF: PBKDF2-HMAC-SHA256, 250 000 iterations, 16-byte random salt.
- Cipher: AES-GCM with a 12-byte random IV. The auth tag is the
PIN-correctness check — there is no separate
pinHashto crib against. - Storage:
localStorageon this device only. Nothing is synced or sent over the network.
A wrong PIN means the GCM auth tag fails and the ciphertext is useless. A 4-digit PIN is still short, so a longer PIN (or no PIN at all) is stronger; we explain the trade-off in SECURITY.md.
The repo is a multi-package monorepo without a workspace tool — each sub-project is independently installable.
# Web app
cd web
yarn install
yarn dev # http://localhost:3000
yarn test
yarn build
# Chrome extension
cd chrome_extension
yarn install
yarn dev # iterate at http://localhost:3000
yarn export # produces ./out/ for sideloading
yarn package # produces offlinepass-extension.zip for the Chrome Web Store
# Flutter app
cd app
flutter pub get
flutter runSee CONTRIBUTING.md for the full guide, including the cross-platform parity rule (any change to password generation must keep the three implementations byte-identical) and the SECURITY.md policy for handling vulnerability reports.
OfflinePass takes a few stances most password managers can't:
- Zero network traffic after the page loads. No analytics, no fingerprinting, no telemetry, no fonts from a CDN, no cookies you didn't ask for. Open the network tab and verify.
- Reproducible derivation. The algorithm is fixed, public, and trivial to re-implement. If we vanish tomorrow, your passwords are recoverable from any HMAC-SHA256 + base58 implementation.
- Single permission. The Chrome extension requests only
activeTab— used solely to read the current tab's URL so the host field can be pre-filled. The URL never leaves your browser.
For the full threat model, what we do not protect against, and how to report a vulnerability privately, see SECURITY.md.
- Web & extension: TypeScript 5, Next.js 16, React 19, MUI 9, Headless UI 2, Tailwind CSS 3, Web Crypto API.
- Mobile: Flutter,
package:crypto,fast_base58,encrypted_shared_preferences. - Crypto primitives: HMAC-SHA256, Base58, AES-GCM, PBKDF2 — all via
Web Crypto on web/extension, all via Dart
package:cryptoon Flutter. No third-party crypto libraries doing key derivation.
Issues, pull requests, and security reports are all welcome. Read CONTRIBUTING.md before starting on a change to the password algorithm — cross-platform parity is non-negotiable, and new tests need to cover all three implementations.
Apache 2.0. See LICENSE.
Built in the open at github.com/sireto/offlinepass. Audit the math, not us.