Skip to content

LastVet iOS Architecture Notes

API client

The veteran app talks to Configuration.apiBaseURL using Bearer JWT session exchange. Networking lives in LastVet/Core/Networking/LastVetAPIClient.swift.

Build LastVetAPIEnvironment API base URL
Debug (scheme LastVet) production https://api.last.vet/api
Release (scheme LastVet archive) production https://api.last.vet/api
Staging (scheme LastVet-Staging) sandbox https://api-staging.last.vet/api

LASTVET_API_ENVIRONMENT is set in checked-in Configuration/*.xcconfig. Session exchange secrets are per-environment in gitignored Secrets.xcconfig (VETERAN_SESSION_EXCHANGE_SECRET_STAGING / _PRODUCTION). Staging tester soak: docs/testflight/MERIDIAN_ASK_STAGING.md.

Imaging storage (envelope encryption)

As of the envelope encryption integration, veteran imaging uploads use Pattern B (API-mediated):

Flow Endpoint Client behavior
Upload POST /veteran/me/documents/imaging/upload Multipart form (file, title, documentType, optional description / documentDate). Plaintext over TLS. Server encrypts with AES-256-GCM + wrapped DEK.
Download GET /veteran/me/documents/:id/download Inline decrypted stream from API (minio_envelope). No client-side object-store URLs.

Implementation:

  • ImagingEnvelopeUpload.swift — multipart temp file + URLSessionUploadTask progress/cancel
  • LastVetAPIClient.uploadImagingDocument — Wi‑Fi guard (>50 MB), envelope POST, error mapping
  • LastVetAPIClient.downloadDocument — inline stream from API (envelope decrypt on server)

Backend requirement

Staging and production API hosts must run with:

IMAGING_STORAGE_MODE=minio_envelope

When envelope mode is active, retired presigned endpoints (/imaging/upload/init, /complete, abandon DELETE) return 410 Gone. The iOS client treats 410 on abandon as success (local dismiss of stale pending-upload banners).

Caddy on lastvet-edge uses 20 minute read/write timeouts to support 512 MiB uploads over slow uplinks.

Limits and UX

  • Server max object size: 512 MiB per imaging study
  • Client Wi‑Fi gate: 50 MiB on cellular (see UploadConnectivity.swift)
  • Upload progress: real percentage via URLSessionUploadTask; user can cancel mid-upload

Further reading

Provider portal (separate repo)

last.vet-provider downloads veteran documents via GET /provider/:id/veteran/:veteranId/documents/:documentId/download and expects an inline Blob. It does not implement the retired veteran presigned multipart upload flow. Envelope streaming downloads should work without provider changes; provider-originated uploads use the standard document POST (non-imaging types only in UI today).

Community tab (content-gated)

Org-side (last1.app register, listing, monthly attest) is Phase 1 outreach. The veteran tab stays off until at least one org has a current attestation. Spec: lastvet-gates/docs/lastvet/COMMUNITY_DIRECTORY_SPEC.md. Do not add a Community tab, Find Care mix-in, or Meridian-style matching until that unlock is real.

Tests

LastVetTests/NetworkingTests/ImagingEnvelopeUploadTests.swift covers multipart temp-file writing, imaging error mapping, and 410 abandon dismiss behavior.