Pass → Koder migration runbook

active

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-the-product (mobile + desktop app) Pass-the-server (lives on for the 90-day window)
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
D+0 (2026-05-20) RFC-001 ratified, this runbook lands
D+0..D+30 #006a Vault engine surface; #009b Pass last-release modal+export
D+30..D+90 #006b-d Vault UI (passwords, documents, import) ships in Koder app
D+90 (2026-08-18) Pass switches to read-only mode (Go server flag)
D+180 (2026-11-16) Pass removed from app stores; repo stays as historical ref
D+360 (~2027-05-15) 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 → Export 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-08-18 — finish your migration to Koder."*

  • [ ] 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, re-encrypts under Koder vault key, persists

  • [ ] First-launch prompt in Vault (post-#006): *"Coming from Koder

    Pass? Import your vault here."*

Cross-cutting

  • [x] This runbook
  • [ ] koder.dev/migrate-from-pass landing (cite this runbook, link

    to 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

No 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 Argon2id-derived 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-week banner reminders pre-D+90 and pre-D+180; D+90 read-only mode preserves read access for an extra 90 days
Transit key typo: import fails Standard error UX; user re-exports from Pass with a memorable key
Pass-server crashes during read-only window 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