Skip to content

ADR 001: Clinic Prescribing vs Patient Sharing Boundary

Status

Accepted

Date

2026-05-20 (revised)

Context

Onset has two distinct outbound use cases:

  1. Clinics prescribe protocols to patients via clinic.theonset.app. Practitioners write protocols, assign them, and patients receive them on iOS.
  2. Patients share their own data with providers. As of v1 this is veteran-only and routed through LastVet — see ADR-002 for that scoping decision.

These look superficially similar (both touch outside parties) but have completely different trust models, write capabilities, audit requirements, and UX language. This ADR draws the boundary so they don't drift into each other.

Decision

We define two operational channels with hard separation between them:

1. Clinic Channel (Prescribing — write)

  • Purpose: create, update, and assign supplement protocols.
  • Actors: clinic staff and practitioners with accounts on clinic.theonset.app.
  • Direction: clinic → patient. Practitioners author; patients receive on iOS.
  • Write capability: protocol authoring, assignment, revision.
  • Auth principal: practitioner Supabase auth, scoped by clinics.id and practitioners.id.
  • Data store: clinic_protocols, protocol_assignments, practitioner_patients, protocol_v2.

2. Patient Sharing Channel (Aggregator push — read of clinic-authored content, write to outside system)

  • Purpose: patient pushes their own protocol and adherence into a third-party system they control, so providers can see it through that system.
  • Actors: the patient on iOS. No clinic staff. No third-party providers calling Onset directly.
  • Direction: Onset (acting on behalf of the patient) → external system. As of v1 the only external system is LastVet.
  • Write capability: writes to the external system are scoped by what the external system permits and what the patient authorized at connect time. Onset writes nothing to its own data on this path.
  • Auth principal: an Onset client identity authenticated against the external system on a per-patient basis (PKCE OAuth at connect time, refresh-token-bearing API calls thereafter).
  • Data store: Onset-side connection records (patient_aggregator_connections) and outbound sync events (data_share_events); patient health data lives in the external system.

Hard Separation Rules

The patient sharing channel MUST NOT: - Create, edit, or delete clinic protocols. - Reassign or modify protocol assignments. - Modify adherence records on Onset's side (read-only of patient's own data). - Be initiated by anyone other than the patient. - Reuse practitioner OAuth or session tokens. - Cause provider-facing UI on Onset (providers do not have Onset accounts; they use the external system or — see ADR-002 — clinic.theonset.app if they want a direct Onset account).

The clinic channel MUST NOT: - Push patient data to external aggregators. - Reuse patient aggregator tokens for prescribing actions. - Read patient adherence except via the existing practitioner-patient consent (which is part of the clinic channel itself, governed by consent_share_* flags).

Domain Model Boundary

Concept Channel Notes
Clinic, Practitioner Clinic Existing.
clinic_protocols, protocol_assignments, protocol_v2 Clinic Existing.
patient_aggregator_connections Patient sharing New. One row per (patient, external_system).
data_share_events Patient sharing New. Append-only audit of every outbound sync attempt.
LastVetClient (registered on LastVet side) Patient sharing Onset's OAuth client identity in LastVet.

A single real-world organization could in principle participate in both channels (e.g. a clinic that also runs a veteran-aggregator integration), but the technical principals and tables remain separate. We do not dual-use credentials.

Why This Decision

  • Keeps prescribing risk surface (writes) isolated from aggregator risk surface (cross-system identity).
  • Keeps Onset's surface area small: we are not running an OAuth authorization server for third-party providers in v1. Providers who want to see Onset data either (a) sign up as a practitioner on clinic.theonset.app or (b) use LastVet's existing provider portal if their veteran patient shared via LastVet.
  • Matches the actual product calls: clinic prescribing is the existing revenue surface; LastVet integration is the next concrete partner.

Consequences

Positive

  • Clean copy in UX: "Practitioner prescriptions" (clinic) vs. "Connected health records" (patient sharing).
  • Auditing is split by table, not by tag — easier to reason about and easier to retention-rule independently.
  • Adding a second aggregator (or a non-veteran sharing path) later is a self-contained extension; nothing in the clinic surface needs to change.

Tradeoffs

  • Two consent UIs, two audit logs, two settings sections on iOS.
  • A patient who is also a clinic patient AND a LastVet user has two distinct sharing relationships to manage. Acceptable; both are explicit choices the patient made.

Enforcement Requirements

  • API authorization middleware tags every request with channel ∈ {clinic, patient_sharing} and rejects cross-channel writes.
  • Patient sharing endpoints are outbound-only from Onset; Onset exposes no inbound provider-facing read API in v1.
  • Audit events include channel and actor_principal_type (practitioner vs patient).
  • iOS Settings has a Clinic section (existing: "My practitioner") and a Sharing section (new: "Connected health records"); copy is channel-specific and never conflated.

What this ADR replaces

This is a revision of an earlier draft that proposed a generic third-party provider OAuth integration with SMART-on-FHIR scopes (PROVIDER_AUTH_SMART_FHIR.md, PROVIDER_DATA_ACCESS_CONTRACT.md). Those are deleted. The narrower v1 scope is documented in ADR-002.