mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 14:22:51 +00:00
142 lines
5.4 KiB
Markdown
142 lines
5.4 KiB
Markdown
# 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 library’s API shape and storage
|
||
expectations.
|