Pass → Koder migration runbook
Pass → Koder migration runbook
Operational plan for retiring Koder Pass as a standalone product and folding its features into the Koder app, per id-app-RFC-001-stack-portal-consolidation.kmd Path B.
Scope
| In scope | Out of scope |
|---|---|
| Pass |
Pass |
| Pass vault data (passwords, documents) | Chromium extension migration (own thread — sees #006e) |
| End-user UX (export + import) | Automated migration (RFC explicitly bans silent key movement) |
| 90/180 day cadence | Customer comms (forum / Slack — owner-driven) |
Timeline
| Day | Event |
|---|---|
| *+0*(2026 |
RFC-001 ratified, this runbook lands |
| *+0..D+30* | #006a Vault engine surface; #009b Pass last-release modal+export |
| *+30..D+90* | #006b-d Vault UI (passwords, documents, import) ships in Koder app |
| *+90*(2026 |
Pass switches to read-only mode (Go server flag) |
| *+180*(2026 |
Pass removed from app stores; repo stays as historical ref |
| *+360*(~2027 |
services/foundation/pass/ archived |
The 90180 cadence is conservative. Owner can compress to 3060 if install base is small (no public analytics; owner has the number).
Per-side checklist
Pass-side (last release)
- [ ] One-time modal on app open: *"Pass is now part of Koder. Your
data stays where you put it — to continue, open Koder."*
- [ ] Settings → *xport to Koder*button.
- [ ] Asks user to type a transit key (fresh passphrase, vault-only)
- [ ] Bundles vault data + metadata into a single file
- [ ] Encrypts the bundle with a key derived from the transit key
(Argon2id with new salt; same KDF that Koder will use to decrypt)
- [ ] Saves to platform storage with a clear filename:
koder-pass-export-<YYYY-MM-DD>.kpex
- [ ] D+90 read-only flag (Go server feature flag, single boolean):
- [ ] All write endpoints return `409 Conflict {"error":
"passreadonlymigrationwindow"}`
- [ ] Reads continue normally
- [ ] Modal copy switches to *"Pass is read-only since
2026
0818 — finish your migration to Koder."*
- [ ] All write endpoints return `409 Conflict {"error":
- [ ] D+180 store removal: pull from Play Store + App Store + the
desktop release feed. Web download links 301 to
koder.dev/migrate-from-pass.
Koder-side (this app)
- [x] Help page reachable at
koder://help/migrate-from-pass(ships with id-app#009 —
lib/sections/help/pass_migration_page.dart) - [x] Vault section banner explains Pass absorption (ships with #006)
- [ ] Import flow in Vault → Import → "From Koder Pass"
(ships with #006d): picks file + asks for transit key, decrypts on
device, reencrypts under Koder vault key, persists - [ ] First
launch prompt in Vault (post#006): *"Coming from KoderPass? Import your vault here."*
Cross-cutting
- [x] This runbook
- [ ]
koder.dev/migrate-from-passlanding (cite this runbook, linkto Help page deep link, show timeline)
- [ ] Forum / Slack announcement on D+0 ratification
- [ ] In-app announcement banner in the Pass app's home screen 2
weeks before D+90
- [ ] In-app announcement banner 2 weeks before D+180
Encryption / key handoff
*o silent migration of encryption material.*The export file is encrypted under a transit key the user types in Pass. The user types the same transit key in Koder to decrypt. Properties:
- Transit key never leaves the user's head/notes — not stored by Pass,
not transmitted, not in the export bundle's plaintext metadata.
- If the device is taken, the export file alone is insufficient: the
attacker would need to either brute
force the Argon2idderived key or coerce the transit key from the user. - Different from "we'll just copy the vault key over" — that would be
silent and would mean a stolen Pass DB grants Koder access too.
- Different from "user signs in fresh and re-enters every credential"
— that loses metadata (last-used, notes, document scans).
Argon2id parameters (same on both sides — pinned in #006a engine surface decision):
- memory: 64 MiB
- iterations: 3
- parallelism: 4
- salt: 16 bytes random per export bundle (in the bundle header)
- output: 32 bytes (used as the symmetric key for XChaCha20-Poly1305
vault bundle encryption)
Risk register
| Risk | Mitigation |
|---|---|
| Users miss the modal, vault becomes inaccessible at D+180 | 2 |
| Transit key typo: import fails | Standard error UX; user re-exports from Pass with a memorable key |
| Pass |
Same server we run today; no new failure mode |
| Chromium extension breaks at D+90 | #006e ships before D+90 — extension switches its API base from pass.koder.dev to `id.koder.devv1me/vault |