Skip to content

[Feature Request] Encrypted storage for passwords + essential info ("My Important Info") — design doc for review #714

Description

@coolshippdx

Companion to #713 (opt-in account recovery) — second of three feature drafts from Shelter Now's lived-experience sessions (Lived Experience Council + Village Federation), posted now so Code PDX can review the set together. Same caveats as #713: draft for discussion, AI-assisted drafting from lived-experience requirements, file references are to Development, nothing here weakens the default posture, and Shelter Now will run paid lived-experience review sessions before any implementation is finalized.


PASS Design Doc — Feature 2: Encrypted Passwords & Essential Info ("My Important Info")

Status: DRAFT (local) — post to codeforpdx/PASS after #713 has a direction
Origin: Shelter Now LEC/VF sessions · Date: 2026-07-16

1. Problem

Testers constantly lose access to email, social media, and benefits accounts
because passwords, case numbers, and key phone numbers live in lost notebooks
and dead phones. They asked for the locker to hold this info. Deliberately
basic: an encrypted notes/key-value section — not a password manager (no
autofill, no browser extension, no breach monitoring).

2. Why this needs real encryption (unlike today's PASS documents)

PASS documents are plaintext in the pod behind WebACL
(src/utils/network/session-core.js). That may be arguable for documents; it
is not acceptable for passwords: pod-provider operators/backups would hold
users' credentials in the clear. This feature introduces PASS's first
client-side crypto, deliberately small:

  • Key derivation: password-derived key via PBKDF2 (Web Crypto, ≥600k
    iterations, per-user salt) from the existing pod password at unlock time —
    no second password to lose. Derived key held in tab memory only.
  • Encryption: AES-256-GCM per entry (random IV per write); ciphertext
    stored as an RDF thing in PASS/EssentialInfo/essentials.ttl in the pod,
    following the useRdfCollection pattern used by the civic profile
    (src/hooks/useCivicProfile.js).
  • No hand-rolled crypto; Web Crypto only, no new deps.

3. Data model

Entry = { id (uuid), label, category, value (encrypted), updatedAt }.
Categories fixed at launch: password, contact, case-number, note.
Only value is encrypted; label/category are visible metadata in the pod
(name labels like "Gmail" are low-sensitivity; revisit if testers disagree).
Cap value length (~2 KB) and entry count (~100) to keep this a notes section,
not a vault.

4. UX (mobile-first, plain language)

  • Nav item: "My Important Info" — list of cards by category; tap to
    reveal a value (auto-hide after 30 s); add/edit via a one-field-at-a-time
    modal, matching the Documents desktop/mobile component split and MUI theme.
  • Unlock prompt re-asks for the pod password when the section is first opened
    in a session (deriving the key); wrong password = generic "couldn't unlock."
  • Inactivity logout (existing InactivityMessage.jsx) also clears the derived key.
  • Reading-level pass on all copy; no jargon ("locked with your password" not
    "encrypted with AES-GCM").

5. Interaction with recovery (the hard question — review gate)

If the account is recovered via Feature 1, the pod password is reset, and a
PBKDF2(pod-password) key can no longer decrypt old entries. Two options:

  • Option 1 (recommended): recovery covers this section too. The section's
    true key is a random 256-bit key, wrapped twice: once by PBKDF2(pod
    password) and — only if the user opted into recovery — once by the recovery
    secret from Feature 1. On recovery, the new password re-wraps the key.
    Consent must say plainly: "the people who can help you get back in could,
    together, also reach your saved passwords."
  • Option 2: recovery excludes this section. Purist, but testers' whole ask
    was durability; losing saved passwords on recovery recreates the original
    problem. Offer at most as a per-entry "extra lock" toggle later.

This interaction must be explicitly reviewed (maintainers + lived-experience
testers) before merge — it is the consent-critical decision of the feature.

6. Threats

Pod/provider breach → ciphertext only. Shared-terminal shoulder-surfing →
tap-to-reveal + auto-hide + inactivity clear. Abuser with the phone → same
exposure as the pod itself; no additional surface. Recovery collusion → named
in consent (Option 1). Lost password without recovery opt-in → entries
unrecoverable (same honest claim as today's pods).

7. Prototype plan

  1. src/utils/crypto/essentialsCrypto.js (derive/wrap/seal/open + tests,
    including wrong-key and tamper tests).
  2. src/model-helpers/EssentialsList.js + hook, mirroring DocumentList.js.
  3. src/components/EssentialInfo/ UI behind VITE_ENABLE_ESSENTIALS flag.
  4. Lived-experience comprehension test: "who can see your saved passwords?"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions