mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 14:22:51 +00:00
5.4 KiB
5.4 KiB
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
Pickerin the main form; defaults topassword. - Switching to key auth shows a
Pickerfor 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
upsertPrivateKeythat invalidates the React Querykeysquery.
UX Objectives
- Remove the inline key
Picker; select a key within the Key Manager modal. - Replace the security type
Pickerwith 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
-
Replace security type
Pickerwith 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.
- Use a
-
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
Pressablethat opens the Key Manager for selection. - Disabled state (no keys): show "None" with hint "Open Key Manager to add a key".
-
Key Manager: add selection mode
- Add a radio-style control on each row to select the key for this session.
- Modal accepts an optional
selectedKeyIdandonSelect(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.
-
Styling parity
- Ensure the read-only "Private Key" field height/padding matches
TextField. - Use consistent typography/colors for labels, values, and hints.
- Ensure the read-only "Private Key" field height/padding matches
-
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.tsxform:- Toggle for auth type.
- Read-only key field that opens modal.
- Update
KeyManagerModal:- Support selection behavior with visual radio and
onSelectcallback. - Keep existing generate/rename/delete/set-default.
- Support selection behavior with visual radio and
Phase 2 — Import Keys
-
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)
-
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.
-
UX details
- Show parse/validation errors inline.
- Set initial
labelfrom 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
-
Default key semantics
- Guarantee exactly one default key at most by flipping
isDefaulton upsert whenisDefault === true. - Consider: separate lightweight persistent "defaultId" in manifest root to avoid iterating all keys on default change.
- Guarantee exactly one default key at most by flipping
-
Robustness
- Add safety around manifest chunk growth: if
manifestChunkSize + newEntrySize > sizeLimit, create a new chunk. - Ensure
createdAtMsis preserved across upserts (done) and addmodifiedAtMsif useful.
- Add safety around manifest chunk growth: if
-
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 thekeysquery). - Prefer modal-based selection with a simple “field-as-button” in the form.
- For future passphrase support, confirm the SSH library’s API shape and storage expectations.