Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

| | |
Expand Down Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions docs/decisions/0001-local-only-architecture.md
Original file line number Diff line number Diff line change
@@ -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.
37 changes: 37 additions & 0 deletions docs/decisions/0002-pdf-export-legal-privacy.md
Original file line number Diff line number Diff line change
@@ -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.
38 changes: 38 additions & 0 deletions docs/decisions/0003-device-encryption-exports-backups.md
Original file line number Diff line number Diff line change
@@ -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.
18 changes: 18 additions & 0 deletions docs/decisions/0004-primary-user-caregiver-role.md
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 29 additions & 0 deletions docs/decisions/0005-grab-and-go-emergency-access.md
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 29 additions & 0 deletions docs/decisions/0006-excluded-field-safety-boundary.md
Original file line number Diff line number Diff line change
@@ -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.
Loading