From 23f763b315d6d37e348e23d0e03149e601ec389a Mon Sep 17 00:00:00 2001 From: Jberma23 Date: Fri, 7 Aug 2026 12:20:15 -0600 Subject: [PATCH] update docs --- README.md | 22 +++++++++++ .../decisions/0001-local-only-architecture.md | 34 +++++++++++++++++ .../0002-pdf-export-legal-privacy.md | 37 ++++++++++++++++++ .../0003-device-encryption-exports-backups.md | 38 +++++++++++++++++++ .../0004-primary-user-caregiver-role.md | 18 +++++++++ .../0005-grab-and-go-emergency-access.md | 29 ++++++++++++++ .../0006-excluded-field-safety-boundary.md | 29 ++++++++++++++ docs/decisions/README.md | 23 +++++++++++ mobile/README.md | 18 ++++++++- 9 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 docs/decisions/0001-local-only-architecture.md create mode 100644 docs/decisions/0002-pdf-export-legal-privacy.md create mode 100644 docs/decisions/0003-device-encryption-exports-backups.md create mode 100644 docs/decisions/0004-primary-user-caregiver-role.md create mode 100644 docs/decisions/0005-grab-and-go-emergency-access.md create mode 100644 docs/decisions/0006-excluded-field-safety-boundary.md create mode 100644 docs/decisions/README.md diff --git a/README.md b/README.md index b4372b9..a46e0c2 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,20 @@ The two halves are independent programs that talk over HTTP. Rails never renders the phone UI, and the phone app is not served by Rails; it is bundled by Metro in development and shipped to the app stores by EAS in production. +> [!IMPORTANT] +> The `Task` round trip above is a **starter-kit scaffold** proving the two +> halves can talk to each other — it is not this product's real data +> architecture. The actual decision (see +> [`docs/decisions/0001-local-only-architecture.md`](docs/decisions/0001-local-only-architecture.md)) +> is that the patient journal — medications, contacts, medical history, care +> preferences — lives **only** in an encrypted local database on the phone, +> never in this Rails API. Rails stays scoped to non-journal concerns (the +> org's public site, a donation page). **Don't copy the `Task`/`/api/v1` +> pattern for a real journal model.** Read +> [`docs/decisions/`](docs/decisions/README.md) — especially 0001 and +> [0006 (excluded fields)](docs/decisions/0006-excluded-field-safety-boundary.md) +> — before adding a new screen or data model. + ## Requirements | | | @@ -154,6 +168,14 @@ Rubocop, Brakeman and the Docker build context all skip `mobile/`, since its `node_modules` ships Ruby CocoaPods scripts that would otherwise be linted and scanned as if they were ours. +## Product & architecture decisions + +Decisions that aren't obvious from the code — what's local-only vs. server-side, +what data is excluded from the app entirely, how exports/backups are meant to +work, who the app is designed for — live in +[`docs/decisions/`](docs/decisions/README.md). Given the volunteer, +rotating-contributor model, read that folder before you read the code. + ## Next steps - **Authentication.** There is none yet. Every endpoint is public. A token diff --git a/docs/decisions/0001-local-only-architecture.md b/docs/decisions/0001-local-only-architecture.md new file mode 100644 index 0000000..e883fa1 --- /dev/null +++ b/docs/decisions/0001-local-only-architecture.md @@ -0,0 +1,34 @@ +# 0001 — Local-only architecture; no backend for journal data in MVP + +**Status:** Decided +**Decided by:** Paul Smith & Jesse Berman (technical leads), per architecture-plan.md review, 7/23–8/2 +**Resolves:** GitHub issues "Resolve architecture direction: Rails API + Expo, offline-capable, or local-only" and "Decide whether the product must work without a network connection" + +## Context + +The repo contains a working Rails 8 API + Expo mobile app talking over HTTP (`/api/v1/tasks`). A separate product brief document described "Rails JSON API consumed by Expo client" as Proposed, with "Offline behavior" listed as Open — which reads as if the network architecture were still undecided. It isn't. That brief language drifted from a decision the team already made in `architecture-plan.md`, the actual document Paul and Jesse reviewed. + +## Decision + +**The journal (medications, contacts, medical history, care preferences, etc.) is local-only for MVP.** No backend, no Postgres, no Rails involvement for journal data. Data lives in an encrypted local SQLite database on the patient's device (encryption decision tracked separately, not duplicated here). There is no remote caregiver login and no server holding this data in MVP. + +Rails stays in the repo, scoped to non-journal purposes only: the org's website/donation page today, and potentially an *optional* future sync layer (see "Future path," below) — never the patient-facing app's medical data in MVP. + +**Corollary — the product must work fully without a network connection.** Since journal data never makes a network call, there's no "offline mode" to design; there's no online mode to fall back from. This removes real scope from Phase 0's error/loading/offline-state work — offline isn't a state to design for the journal screens, it's just how the app always behaves. + +## Rationale + +- Matches the stated MVP principle: "the point of local-only storage is that Along with You's servers never hold this data at all, not that the app happens to work offline" (architecture-plan.md). +- Matches Paul's separately documented position of being "extremely hesitant about doctor access" as a live/connected feature, preferring a printable PDF export instead (see [0002](0002-pdf-export-legal-privacy.md)). A networked API serving live journal data would cut against that decision, not just be unrelated to it. +- The Rails API currently in the repo is a starter-kit scaffold (`Task` is explicitly a placeholder resource per `README.md`), not evidence of an actual decision to route real medical data through a network API. A PR review already found it unauthenticated, unbounded, and open to any origin in dev — acceptable for a demo, not for medications and advance directives. + +## Consequences + +- No offline queue, no optimistic-update/conflict-resolution logic, no "pending sync" UI state needed for MVP. +- **Dependency audit needed:** confirm no third-party SDK (crash reporting, analytics, Expo's EAS Update check) blocks app launch waiting on a network call. EAS Update in particular checks for updates on launch by default — needs to be configured non-blocking so a patient in a dead zone isn't stuck on a spinner before seeing their own medication list. +- Section 7 of `architecture-plan.md` keeps a documented future path (end-to-end encrypted sync, or simpler server-managed encryption via Rails/Postgres) open for later, if the org ever wants backup/multi-device/remote-caregiver access. Not ruled out forever — just not MVP, and not a default to drift back into without a new explicit decision. + +## Open follow-up + +- Write the formal ADR sign-off from both Paul and Jesse referencing this record (this file *is* that record — close the "write an ADR" backlog ticket by pointing here). +- Correct the source product brief's phase table so "Rails API — Proposed" / "Offline behavior — Open" stop reading as live questions. diff --git a/docs/decisions/0002-pdf-export-legal-privacy.md b/docs/decisions/0002-pdf-export-legal-privacy.md new file mode 100644 index 0000000..dcf2a38 --- /dev/null +++ b/docs/decisions/0002-pdf-export-legal-privacy.md @@ -0,0 +1,37 @@ +# 0002 — Legal/privacy basis for printable & emailed PDF export + +**Status:** Research complete and decided; a plain-language privacy policy still needs to be written before ship +**Resolves:** GitHub issue "Research legal/privacy implications of printable or emailed PDF exports" +**Unblocks:** the doctor-sharing / printable-summary export ticket + +## Context + +Paul flagged that print/email export of medical data needed legal research before being built. This mainly concerns the printable PDF summary (medications, insurance, vitals, therapy log) that Paul proposed as the alternative to live doctor-account access (see [0001](0001-local-only-architecture.md)). + +## What applies to us + +- **HIPAA does not apply.** HIPAA only binds "covered entities" (healthcare providers, health plans, clearinghouses) and their contracted business associates. Along With You is neither — we're not built on behalf of a provider. This would change if we ever integrated with a provider's system directly. +- **The FTC Health Breach Notification Rule does apply.** It covers health apps not covered by HIPAA that handle identifiable health record data (updated by the FTC in 2024). If user health data is ever exposed in a breach, we may have a legal duty to notify affected users, the FTC, and possibly media. +- **State consumer health data laws likely apply**, based on where users are, not where we're based — we'll have users in multiple states as a public app. Washington's My Health My Data Act requires a standalone consumer health data privacy policy plus consent before collecting/sharing data. California's CMIA restricts using or disclosing health data for anything beyond what's needed for the person's care. +- **Bottom line:** not being a hospital doesn't exempt us from privacy law. We need a plain-language privacy policy describing what health data we collect and how export/sharing works, before the export feature ships. + +## Risks specific to PDF export/email + +1. Email isn't secure by default — interception, misdelivery, unencrypted storage on mail servers, forwarding by the recipient are all possible once a PDF leaves the app. +2. PDF metadata can leak information the user didn't mean to share (author name, device details, file history). +3. Printed pages are physical artifacts — left on a shared printer, in a bag, on a counter. +4. On shared/family devices, the export flow itself (generate → open → send) is a bigger exposure window than just viewing the journal in-app. + +## Decision / required mitigations + +- Warn the user in plain language before export/email: once it leaves the app, we can't protect it. +- Strip PDF metadata at generation time — no author name, device ID, or app internals embedded. +- Require the user to type/select the recipient manually — no pre-filled or suggested addresses, to cut misdelivery risk. +- Default the export to only what's needed for the visit, not the full journal history. +- Add a short, plain-language consumer health data privacy policy (single page) before shipping export. +- Keep export a deliberate, reviewable action — no auto-attach/auto-send. +- **Do not make PDF password protection default or required** — see [0003](0003-device-encryption-exports-backups.md) for why. + +## Not legal advice + +This is engineering-level research, not a legal opinion. Recommend a lawyer review the privacy policy language and breach-notification exposure before ship. diff --git a/docs/decisions/0003-device-encryption-exports-backups.md b/docs/decisions/0003-device-encryption-exports-backups.md new file mode 100644 index 0000000..7fe7f16 --- /dev/null +++ b/docs/decisions/0003-device-encryption-exports-backups.md @@ -0,0 +1,38 @@ +# 0003 — Device-level encryption for exports, backups, and OS auto-backup + +**Status:** Decided; implementation not yet built +**Resolves:** GitHub issue "Research local device encryption for exports and backups" +**Scope note:** this is distinct from the local-database-encryption decision (SQLCipher + biometric unlock, tracked in `architecture-plan.md`), which protects data at rest inside the app. This record covers what happens once data leaves that encrypted store — export, print, email, or backup. Encrypting the database does not automatically protect a PDF, backup file, or printout generated from it. + +## Risk 1 — OS-level backups may copy our data to the cloud without an explicit feature doing it + +Both iOS and Android back up an app's local files by default (iCloud Backup / Google Auto Backup) — no explicit upload action required. This quietly contradicts "no cloud storage of medical data in MVP" even though we never built a cloud feature. + +**Decision:** exclude our data directory from OS auto-backup outright, regardless of the export feature. + +- **iOS:** flag files/directories with `NSURLIsExcludedFromBackupKey` to opt out of iCloud/iTunes backup. +- **Android:** `android:allowBackup="false"` opts the whole app out, or a `backup_rules.xml` can exclude specific paths for more granularity. + +This is a small config change that makes our "local-only" claim actually true rather than true-in-spirit. Should be done early, independent of any export/backup feature timeline. + +## Risk 2 — should a user-initiated backup/restore file (moving to a new phone) be encrypted? + +If/when a "back up my journal" / "restore on new phone" feature is built: technically straightforward with `expo-crypto` (AES-256). The hard part isn't the encryption, it's the passphrase — someone has to remember it, or we store a recoverable key somewhere, which just relocates the security problem. "You forgot your passphrase, your journal is now permanently unreadable" is a real support burden for this audience. + +**Decision:** if this feature is built, encrypt the file, but scope it as direct device-to-device transfer (AirDrop, Files app, direct share) rather than something meant to be emailed or stored elsewhere. Treat a forgotten passphrase as an accepted, clearly-communicated risk rather than something to engineer around (e.g. no "recover my passphrase" flow). + +## Risk 3 — should the PDF export ([0002](0002-pdf-export-legal-privacy.md)) be password-protected? + +AES-256 PDF password protection is genuinely strong when implemented correctly. But: it's only as strong as the password (weak passwords are crackable in seconds), some PDF viewers don't enforce permission restrictions uniformly, and — the real issue for us — sharing a password means a *second* channel to communicate it securely, which adds a step for an exhausted caregiver without eliminating the core interception/misdelivery risk. + +**Decision:** don't make PDF password protection default or required. Keep it optional for users who want the extra step, with a one-line explanation of the tradeoff. Primary mitigation for print/email export stays what's in [0002](0002-pdf-export-legal-privacy.md): the pre-export warning, no pre-filled recipients, metadata stripping. + +## Summary + +| What | Decision | +|---|---| +| OS auto-backup (iCloud/Google) of app data | Exclude entirely — do this regardless of other export work | +| Device-transfer backup/restore file | Encrypt (AES-256), device-to-device only, forgotten passphrase = accepted unrecoverable risk | +| PDF export password protection | Optional, not default — passphrase-sharing problem outweighs the benefit for this audience | + +Worth a second pair of eyes from someone with mobile security experience before locking in the backup-file encryption implementation. diff --git a/docs/decisions/0004-primary-user-caregiver-role.md b/docs/decisions/0004-primary-user-caregiver-role.md new file mode 100644 index 0000000..2c90ad3 --- /dev/null +++ b/docs/decisions/0004-primary-user-caregiver-role.md @@ -0,0 +1,18 @@ +# 0004 — Primary MVP user and caregiver role + +**Status:** Decided, with open sub-questions +**Resolves:** GitHub issue "Identify the primary MVP user and any caregiver role" + +## Decision + +**Single device, single shared journal, no caregiver account or role distinction in MVP.** Per `architecture-plan.md`'s guiding constraints: "The product is a personal journal a patient keeps on their own device. A caretaker or doctor views it in person, over the patient's shoulder or by being handed the phone, not through a remote account." Combined with "no remote caregiver login and no server in the MVP" ([0001](0001-local-only-architecture.md)), this means: whoever holds the patient's phone — patient or caregiver — sees the same journal, the same way. There is no separate caregiver account, role, or permission tier. + +## Still open + +- **Does a caregiver ever use their own device instead of the patient's?** E.g. a patient too impaired to operate a phone at all, where the caregiver is functionally the sole user, on their own hardware. The current model assumes one journal lives on one shared device — it doesn't address a caregiver-only user who never touches the patient's phone. Needs a decision if this scenario is in scope for MVP. +- **Tone/pronoun handling in copy.** Since either a patient or caregiver could be filling in fields, "your medications" may read oddly for a caregiver typing on someone else's behalf. Recommend neutral phrasing ("their," "the patient's") rather than assuming "you" is always the patient — decide once, apply everywhere, rather than per-screen. +- **Multi-patient use is out of scope.** Nothing in the docs suggests a caregiver should manage more than one person's journal (e.g. a professional caregiver, or someone caring for two family members) — worth stating explicitly as out of scope for MVP rather than leaving it merely unaddressed. + +## Why this matters for future screens + +Any screen design that assumes "the user" is always the patient (or always the caregiver) is making an assumption this record explicitly does not support. Default to phrasing and flows that work regardless of who's holding the phone. diff --git a/docs/decisions/0005-grab-and-go-emergency-access.md b/docs/decisions/0005-grab-and-go-emergency-access.md new file mode 100644 index 0000000..ed9590b --- /dev/null +++ b/docs/decisions/0005-grab-and-go-emergency-access.md @@ -0,0 +1,29 @@ +# 0005 — What "grab and go" access means digitally + +**Status:** Decided +**Resolves:** GitHub issue "Define what 'grab and go' access means digitally" + +## Context + +Two different scenarios were being bundled into one screen design question. They have different security requirements, and one of them is already solved by the phone's OS. + +## Scenario A — true emergency, patient can't unlock the phone + +Unconscious or incapacitated patient, first responder has the device. **iOS Medical ID** and **Android's Emergency Information** are built exactly for this: + +- **iOS:** reachable from the lock screen with no passcode/Face ID, via the Emergency button (older iPhones) or the Medical ID slider from the SOS screen (iPhone X+), if the user has "Show When Locked" enabled. +- **Android:** Settings → Safety & emergency → Medical information, shown on the lock screen without unlocking if "Show when locked" is on. Shows blood type, allergies, medications, conditions, emergency contacts. + +Both are OS-maintained and what first responders are actually trained to check. + +## Scenario B — patient or caregiver wants their own info fast + +Not an emergency — a pharmacy counter, a waiting room. Doesn't need to bypass the lock; needs to be the first thing visible after normal biometric unlock, not buried in navigation. + +## Decision + +- **Do not build a custom pre-unlock screen.** Add a short onboarding nudge pointing users to set up their phone's built-in Medical ID / Emergency Information — the right tool for the true unconscious-emergency case, zero build cost, already what EMTs check. +- **Build "Keep with me" as the default landing screen immediately after biometric unlock.** Full protection, no separate credential, satisfies the "fast access for planned use" case. +- **No separate limited credential** (a "lighter" PIN). Adds a second thing to remember and a second flow to build, for a case the OS already covers better. + +Reachable **after** normal unlock, as the first screen — not before, not via a separate credential. diff --git a/docs/decisions/0006-excluded-field-safety-boundary.md b/docs/decisions/0006-excluded-field-safety-boundary.md new file mode 100644 index 0000000..cd99633 --- /dev/null +++ b/docs/decisions/0006-excluded-field-safety-boundary.md @@ -0,0 +1,29 @@ +# 0006 — Excluded-field safety boundary + +**Status:** Boundary is settled; needs formal named sign-off recorded +**Resolves:** GitHub issue "Approve the excluded-field safety boundary" + +## Decision + +The MVP explicitly excludes the following fields, even though they appear in the paper journal this app is based on: + +- **Social Security numbers** +- **Medical portal passwords/secrets** (and usernames — see inconsistency below) +- **Security alarm codes** +- **Exact house key / wallet / purse locations** + +Check every new feature or field suggestion against this list. + +## Rationale, for the sign-off record + +- **SSN:** materially bigger liability than the rest of the journal if the device is lost or compromised, and it has a legal dimension beyond device risk. Every US state's data breach notification law treats name + SSN as a "triggering" combination requiring notification, and several states require 18 months of free identity-theft protection for affected individuals when SSNs are involved. Excluding SSN entirely keeps the app out of that trigger category, not just off the digital form. +- **Medical portal passwords:** the paper journal stores these in plaintext today. Carrying that into the app — even encrypted at rest — makes it a live credential to a real medical account, a categorically different risk than journal text. A leaked password here doesn't just expose our data, it exposes whatever the portal itself guards. +- **Security alarm code / exact house key & wallet/purse location:** not medical data — home-security data traveling with a medical journal. Same device-compromise exposure as the rest, for a benefit the app doesn't need to provide. + +## Known inconsistency to resolve in the same sign-off + +The Phase 1 "Medical Portals" ticket already goes further than this stated boundary: it specifies "name/URL only, no login or password fields" — dropping the *username* too, not just the password. The formal boundary text above only names "passwords/secrets." Recommend extending the boundary to explicitly exclude portal usernames as well, matching what's already assumed in the Phase 1 ticket (a username without a password is low risk alone, but pairing it with portal name/URL still tells someone exactly which account to target). + +## Sign-off + +Needs a named approver on record — recommend Roberta Talmage (product owner) plus both technical leads (Paul Smith, Jesse Berman) — so this doesn't get re-litigated per-field as each screen gets built. Once signed off, close the "track resolution of unapproved sensitive fields" risk-tracking ticket. diff --git a/docs/decisions/README.md b/docs/decisions/README.md new file mode 100644 index 0000000..272899b --- /dev/null +++ b/docs/decisions/README.md @@ -0,0 +1,23 @@ +# Architecture & Product Decisions + +This folder is a lightweight decision log (ADR-style) for choices that aren't obvious from reading the code, and that a new volunteer contributor would otherwise have to reconstruct from old GitHub threads. Given the team's rotating-contributor model, the goal is: if you're new here, read this folder before you read the code. + +Each record has a status. **Decided** means treat it as settled — don't re-litigate it in a PR review, raise a new ticket if you think it's genuinely wrong. **Open** means it still needs a named owner to sign off. + +| # | Decision | Status | +|---|---|---| +| [0001](0001-local-only-architecture.md) | Local-only architecture; no backend for journal data in MVP | Decided | +| [0002](0002-pdf-export-legal-privacy.md) | Legal/privacy basis for printable & emailed PDF export | Decided (research complete; policy copy still needed) | +| [0003](0003-device-encryption-exports-backups.md) | Device-level encryption for exports, backups, and OS auto-backup | Decided (implementation not yet built) | +| [0004](0004-primary-user-caregiver-role.md) | Primary MVP user and caregiver role | Decided, with open sub-questions | +| [0005](0005-grab-and-go-emergency-access.md) | What "grab and go" access means digitally | Decided | +| [0006](0006-excluded-field-safety-boundary.md) | Excluded-field safety boundary (SSN, portal passwords, alarm code, key/wallet location) | Needs formal sign-off | + +## Related, not duplicated here + +- Local database encryption (SQLCipher, biometric unlock) was decided separately and lives in `architecture-plan.md` in the team's shared docs — not repeated here since it's a separate decision from what this folder covers (data *leaving* the device vs. data at rest). +- Full content/data model audit (all 11 paper-journal sections, phased ticket backlog) lives in the team's `project-plan-and-tickets.md` — this folder covers cross-cutting product/legal/architecture decisions, not the per-screen field list. + +## Adding a new record + +Copy the format of an existing file: Status, Context, Decision, Rationale, Consequences / open questions. Number sequentially. Keep it short enough that someone reads the whole thing in under two minutes — if it's getting long, it's probably two decisions, not one. diff --git a/mobile/README.md b/mobile/README.md index 70a2ff4..404dc8f 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -1,7 +1,10 @@ # Along With You mobile app The React Native client, built with Expo SDK 57, TypeScript and Expo Router. -The Rails application in the repository root is the backend. +The Rails application in the repository root currently backs the `Tasks` demo +screen, but **it is not the backend for the real patient journal** — see +[Talking to Rails](#talking-to-rails) below and +[`docs/decisions/0001-local-only-architecture.md`](../docs/decisions/0001-local-only-architecture.md). ## Prerequisites @@ -152,7 +155,16 @@ Release builds have no Metro server to ask, so they require it. The **Tasks** tab is a working example of the round trip: it lists, creates, toggles and deletes records through `/api/v1/tasks`. `Task` is a placeholder -resource, meant to be replaced with your real model. +resource — but it's a placeholder for *this client/server round-trip pattern*, +not a template to copy for real journal data. Per +[`docs/decisions/0001`](../docs/decisions/0001-local-only-architecture.md), the +actual medications/contacts/medical-history models are local-only (on-device +SQLite, encrypted at rest), with no Rails involvement. If you're adding a real +data model, check [`docs/decisions/`](../docs/decisions/README.md) first — +0001 for where the data lives, and +[0006](../docs/decisions/0006-excluded-field-safety-boundary.md) for fields +that must not be collected at all (SSNs, portal passwords, alarm codes, exact +key/wallet locations). ## Tests @@ -191,6 +203,8 @@ Three things to know before writing more: ## Documentation +- [`docs/decisions/`](../docs/decisions/README.md) — product & architecture + decisions (read this first) - [Expo documentation](https://docs.expo.dev/versions/v57.0.0/) - [Expo Router](https://docs.expo.dev/router/introduction/) - [React Native documentation](https://reactnative.dev/docs/getting-started)