OfflinePass
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.
Why OfflinePass
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 |
How it works
const msg = "github.com|[email protected]|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$87booSaeaKYnhgEq
That'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.
Try it in 30 seconds
- 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.
Repository layout
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.
Optional: save your Master Key with a PIN
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.
Develop locally
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 run
See 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.
Security
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.
Tech stack
- 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.
Contributing
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.
License
Apache 2.0. See LICENSE.
Built in the open at github.com/sireto/offlinepass. Audit the math, not us.