Files
fressh/docs/archive/key-management-plan.md
EthanShoeDev c0d70ad99c docs
2025-09-12 22:43:53 -04:00

5.4 KiB
Raw Blame History

Key Management UX & Tech Plan

Goal: Make SSH private key management clear and consistent: selecting a key, generating/importing keys, renaming, deleting, and setting a default — all with a unified, styled UI and predictable data semantics.

Current State (Summary)

  • Security type uses a Picker in the main form; defaults to password.
  • Switching to key auth shows a Picker for keys (awkward empty state) and a “Manage Keys” button.
  • Keys can be generated, renamed, deleted, set as default inside a modal.
  • Storage uses a chunked manifest in expo-secure-store (works for >2KB values) with metadata (priority, createdAtMs, label?, isDefault?).
  • All key mutations go through one upsertPrivateKey that invalidates the React Query keys query.

UX Objectives

  • Remove the inline key Picker; select a key within the Key Manager modal.
  • Replace the security type Picker with a simple toggle/switch.
  • Handle the “no keys yet” case gracefully by auto-opening the modal when user chooses key auth.
  • Keep visual styling consistent with text inputs (button heights, paddings, colors).
  • Prepare the modal for importing private keys (paste text or select file) in a future phase.

Phase 1 — Selection UX + Styling

  1. Replace security type Picker with toggle

    • Use a SwitchField (styled like inputs) labeled "Use Private Key".
    • Value mapping: off → password; on → key.
    • When toggled ON and there is no selected key and no keys exist, auto-open Key Manager.
  2. Remove inline key Picker

    • Replace with a read-only field styled like inputs: label: "Private Key", value: current key label or "None".
    • The field is a Pressable that opens the Key Manager for selection.
    • Disabled state (no keys): show "None" with hint "Open Key Manager to add a key".
  3. Key Manager: add selection mode

    • Add a radio-style control on each row to select the key for this session.
    • Modal accepts an optional selectedKeyId and onSelect(id) callback.
    • When user taps a row (or radio), call onSelect(id) and close the modal.
    • Continue to support generate/rename/delete/set-default; selection should update live.
  4. Styling parity

    • Ensure the read-only "Private Key" field height/padding matches TextField.
    • Use consistent typography/colors for labels, values, and hints.
  5. Empty states

    • If no keys: show a friendly empty state in modal with primary action "Generate New Key" and secondary "Import Key" (Import lands in Phase 2).

Deliverables (Phase 1)

  • Update apps/mobile/src/app/index.tsx form:
    • Toggle for auth type.
    • Read-only key field that opens modal.
  • Update KeyManagerModal:
    • Support selection behavior with visual radio and onSelect callback.
    • Keep existing generate/rename/delete/set-default.

Phase 2 — Import Keys

  1. Import entry points in modal

    • Add "Import" button/menu in the modal header or as secondary action in empty state.
    • Options:
      • Paste PEM text (multiline input)
      • Pick a file (use expo-document-picker)
  2. Validation + Storage

    • Validate PEM or OpenSSH formats; detect supported types (rsa/ecdsa/ed25519/ed448/dsa).
    • Optional passphrase field (store encrypted if supported by library; otherwise prompt each use — confirm feasibility with SSH lib).
    • On success, call the single upsertPrivateKey({ keyId, value, metadata }) and close import flow.
  3. UX details

    • Show parse/validation errors inline.
    • Set initial label from filename (file import) or "Imported Key" (paste); allow editing label on success or selection.

Deliverables (Phase 2)

  • Modal import screen(s) with paste/file flows.
  • PEM validation utility and error handling.

Phase 3 — Data Model + Semantics

  1. Default key semantics

    • Guarantee exactly one default key at most by flipping isDefault on upsert when isDefault === true.
    • Consider: separate lightweight persistent "defaultId" in manifest root to avoid iterating all keys on default change.
  2. Robustness

    • Add safety around manifest chunk growth: if manifestChunkSize + newEntrySize > sizeLimit, create a new chunk.
    • Ensure createdAtMs is preserved across upserts (done) and add modifiedAtMs if useful.
  3. Logging + Dev ergonomics

    • Gate verbose logs behind a debug flag to avoid log spam in production builds.

Phase 4 — Edge Cases & Polish

  • Deleting the currently-selected key: clear selection and show hint to pick/create a new key.
  • Auto-select the default key when switching to key auth and no key is selected.
  • Handle failures from SecureStore (permissions/storage full) with user-friendly messages.
  • Handle very large keys with chunking (already supported), surface an error if size exceeds safe thresholds.
  • Accessibility: ensure labels/roles for controls in modal and the selection field.

Phase 5 — Testing & QA

  • Unit test PEM parsing/validation (Phase 2).
  • E2E flows:
    • First run → toggle to key → auto-open modal → generate key → selected → connect.
    • Import key by paste/file; rename; set default; delete.
    • Delete selected key; confirm the form state updates.

Implementation Notes

  • Keep a single write path for keys: upsertPrivateKey (invalidates the keys query).
  • Prefer modal-based selection with a simple “field-as-button” in the form.
  • For future passphrase support, confirm the SSH librarys API shape and storage expectations.