Skip to content

LastVet Integration — Implementation Plan

Objective

Ship the Onset → LastVet sync described in ONSET_TO_LASTVET_SYNC_CONTRACT.md as the first (and only v1) patient-sharing path. This is the actual rollout plan. The earlier doc with this filename described a generic provider-network rollout that is no longer the plan — see ADR-002.

Scope summary

  • Onset side: iOS connect/revoke UI, OAuth dance, encrypted token storage, sync scheduler, audit table, error/reconnect surfacing.
  • LastVet side: OAuth server endpoints, oauth_clients + oauth_authorization_grants tables, /me/sync source-derivation, two new consent categories, provider-portal "Supplements & Protocols" view, "Connected apps" view in LastVet iOS.
  • Out of scope: civilian sharing, third-party-provider access to Onset, FHIR projection, snapshot/PDF share, second aggregator.

Phases

Phase 0 — Design freeze

Owner: Engineering lead. Inputs: ADR-001, ADR-002, sync contract, LastVet API changes doc, connect/revoke doc, audit doc. Exit criteria: - All five docs reviewed and signed off by Onset engineering and LastVet engineering. - LastVet legal/compliance sign-off that adding 'onset' as a source and adding supplements / protocol_adherence as consent categories does not require updates to LastVet's existing BAAs or partner agreements. (Likely no — Onset is patient-controlled outbound, not a covered third party.) - iOS engineering confirms ASWebAuthSession + Keychain plan is implementable on iOS 17+. - Threat model walkthrough: token theft, replay, refresh-token rotation drift, dual-revoke race conditions.

Phase 1 — LastVet side: OAuth server + /me/sync admit 'onset'

Owner: LastVet API team. Work: - Migration 020_oauth_clients_and_grants.sql. - New last.vet-api/src/oauth/ module: token mint, verify, rotate, revoke. Reuse provider-session.service.ts as template. - New routes: GET /api/v1/oauth/authorize, POST /api/v1/oauth/token, DELETE /api/v1/oauth/token. Authorize page can be a tiny dedicated Next.js route on last.vet-web, or a server-rendered page from the API directly — call to be made by LastVet team. - Auth middleware: JWT path + existing header path coexist. - /me/sync derives source from authenticated principal. - Add supplements and protocol_adherence to CONSENT_CATEGORIES. - Seed oauth_clients row for onset-ios (sandbox + production rows distinct).

Exit criteria: - Postman collection runs end-to-end: authorize → exchange code → POST /me/sync with source=onset → row appears in health_record_items with source='onset' → revoke → next sync 401. - Existing LastVet iOS regression suite passes unchanged (header auth still works). - Sandbox environment available with onset-ios test client credentials.

Phase 2 — Onset side: iOS connect + initial sync

Owner: Onset iOS team. Work: - iOS Settings → Connected health records section, gated to veterans. - ASWebAuthSession integration to LastVet's authorize endpoint. - iOS Keychain storage for tokens, scoped WhenUnlockedThisDeviceOnly. - Onset Supabase: patient_aggregator_connections table + RLS policies (patient can only see their own row). - Onset Supabase: data_share_events table + RLS + append-only triggers. - LastVetSyncService Swift module: build payload from legacy UserProtocolItem when protocolEngineV2 is off, or from StoredProtocol DTO items when on (same source_key shape), plus ProtocolCompletion + LabResult, POST to /me/sync, log to data_share_events. - Initial sync on connect: full snapshot + last-90-day adherence. - Error surfacing: reconnect_required state, banner copy.

Exit criteria: - A test veteran on Onset can tap Connect, complete the OAuth flow against LastVet sandbox, and see their protocol items appear in LastVet sandbox's health_record_items. - Disconnect from Onset works: token revoked at LastVet, no further syncs. - Disconnect from LastVet sandbox works: Onset surfaces reconnect_required within one sync cycle. - Onset audit table has a paired sync_attempted + sync_completed row per attempt.

Phase 3 — Background sync + provider-side surface

Owner: Onset iOS + LastVet provider-portal team. Work: - BGAppRefreshTask scheduled for 22:00 local; foreground catchup if last_synced_at > 24h. - LastVet provider portal "Supplements & Protocols" view, gated by consent. - LastVet iOS "Connected apps" surface. - Onset Settings activity list, reading from data_share_events (last 90 days).

Exit criteria: - A test provider on provider.last.vet sandbox sees the connected veteran's Onset protocol and adherence sparkline. - Background sync runs reliably overnight; verified by a 7-day soak with N=10 internal testers. - Activity list renders correctly across success, failure, and revoke cases.

Phase 4 — Closed pilot

Owner: Product + LastVet account management. Work: - Recruit 10–25 dual-app veterans (Onset + LastVet) who have a connected provider on LastVet. - Daily monitoring: success rate, p95 latency, denial events, support tickets. - Bi-weekly product reviews with at least three pilot veterans and one pilot provider.

Exit criteria: - Sync success rate ≥ 99% over a rolling 7-day window. - Connection success rate (initiated → connected) ≥ 95%. - Zero confirmed cases of revoke-not-honored (either direction). - No critical security findings. - At least three pilot providers have viewed Onset-sourced data and confirmed it's useful in their workflow.

Phase 5 — General availability for all veterans

Owner: Product. Work: - Remove pilot gate; LastVet card visible for all veterans on Onset. - In-app announcement to existing veteran users. - Migration playbook: how to migrate the seeded onset-ios client to a long-term key rotation schedule (RS256 + JWKS).

Exit criteria: none stricter than Phase 4 KPIs sustained for 30 days.

KPI targets

KPI Target
OAuth dance success rate ≥ 95%
Sync success rate (7-day rolling) ≥ 99%
p95 /me/sync latency ≤ 1.5s for ≤ 50-item payloads
Background sync reliability (synced within 25h of prior) ≥ 98%
Revoke effective time (either direction) ≤ 5 minutes
100% of sync attempts logged in data_share_events yes

Risk register

  • OAuth implementation correctness. First time LastVet runs an OAuth server. Mitigation: HS256 with a single client until a second appears; lean on the existing provider-session.service.ts patterns; explicit threat-model review in Phase 0; keep tokens short-lived.
  • Refresh token leakage on stolen device. Mitigation: WhenUnlockedThisDeviceOnly keychain attribute; refresh tokens rotate; explicit "Disconnect on lost device" instruction in support docs (the veteran can revoke from inside LastVet on a different device).
  • Patient confusion about three sharing relationships. A veteran on Onset can have (a) a practitioner via clinic, (b) a connected LastVet, (c) providers connected through LastVet. Mitigation: copy split — clinic surface uses "practitioner", LastVet surface uses "providers", and we never mix the words. Settings sections are visually separated.
  • LastVet API outages cause silent data loss. Mitigation: failed syncs retry on next schedule, and the activity list shows the failure to the patient. We do not auto-purge unsynced data; it stays in Onset and re-syncs when LastVet is reachable.
  • Schema drift between Onset and LastVet payload shape. Mitigation: pin the contract version in X-Onset-Client-Version; LastVet reads it and applies a per-version validator. Version bump = explicit doc change + paired client/server release.
  • Veteran self-identification is wrong. Someone marks themselves a veteran in Onset who isn't, then connects to LastVet. LastVet's own auth is the source of truth — they can't successfully connect if LastVet rejects their identity. Mitigation: rely on LastVet's auth; Onset's isVeteran flag is only used to decide whether to show the card.
  • Background sync battery / network impact. Mitigation: BGAppRefreshTask with reasonable cadence, batched payloads, exponential backoff on failures.

Backward compatibility

  • Existing practitioner→patient adherence push (shared_adherence on Onset's clinic side) keeps running. The LastVet sync is an additional data path, not a replacement.
  • Patients who never connect to LastVet see no behavior change.
  • LastVet's existing iOS app continues to work via its Phase 1 bootstrap auth headers; the OAuth path is new and parallel.

What this plan replaces

The earlier doc at this path described a multi-provider OAuth/SMART-FHIR rollout. That's no longer the plan — see ADR-002. This file is now the actual rollout plan for the LastVet integration.