Docs/Privacy & PII Config
Privacy

Privacy & PII Configuration

What data VetRx Ledger stores, how sensitive fields are encrypted, and how to operate in minimal-PII mode to protect staff and patient privacy.

Design principle: VetRx Ledger is built for minimal PII from day one. The system records what happened to which vial at what time by which role — not who the patient was. Patient-level clinical data stays in your EMR; this system manages DEA-required substance records only.

What data VetRx Ledger stores

FieldTypeEncrypted at restNotes
emailAccount / waitlist✅ AES-256-GCMNever shared with third parties
dea_numberOrg/clinic record✅ AES-256-GCMRequired for DEA-106 draft generation
dvm_id, tech_idLedger eventsNo (indexed for query)Can be badge numbers, initials, or role codes — not required to be real names
org_name, clinic_nameOrganisation recordNoNot PII unless the name contains a personal name (e.g. "Dr. Smith's Animal Clinic")
drug, lot, expiry, qtyVial / event recordNoDrug inventory data only — not PII
Patient name / client nameNot collected — intentionally out of scope

AES-256-GCM field-level encryption

Sensitive fields are encrypted before they reach the data store using AES-256-GCM (Galois/Counter Mode), which provides both confidentiality and authenticated integrity. The implementation lives in lib/encrypt.ts.

  • Key: 256-bit (32-byte) key stored as a 64-character hex string in the FIELD_ENCRYPTION_KEY environment variable on the server.
  • IV: A random 12-byte initialisation vector is generated per encryption operation and prepended to the ciphertext.
  • Authentication tag: A 16-byte GCM authentication tag is included — any tampering with the ciphertext causes decryption to fail with an explicit error.
  • Storage format: base64(iv[12] + tag[16] + ciphertext)
  • Idempotent: encryptField detects already-encrypted values and skips re-encryption, preventing double-encryption bugs.
Key loss = data loss. Encrypted fields cannot be recovered without the original FIELD_ENCRYPTION_KEY. Store the key securely (password manager, secret management service) and back it up. Rotation requires re-encrypting all stored values.

Generating a production key

openssl rand -hex 32
# Example output: a3f8d2...64 hex characters total
# Set this as FIELD_ENCRYPTION_KEY in your Vercel environment:
vercel env add FIELD_ENCRYPTION_KEY

Minimal-PII mode

VetRx Ledger does not enforce real names anywhere in the system. To operate in maximum-privacy mode:

  • Use badge numbers or role codes for dvm_id and tech_id on every event (e.g. DVM-04, TECH-11). Maintain a separate, access-controlled roster that maps badge numbers to real identities — VetRx Ledger never needs it.
  • Use a functional email address for the account rather than a personal one (e.g. compliance@yourclinic.com).
  • Use a clinic identifier rather than a personal name for the DEA registrant field (e.g. "Riverside Animal Hospital — Unit 2"rather than a DVM's personal name, where the registration permits it).
  • Review your state board requirements — some jurisdictions require the individual practitioner name on DEA records. VetRx Ledger supports either approach.

Data retention

VetRx Ledger uses two storage layers depending on your deployment:

  • Serverless ephemeral storage (/tmp/vetrx-*.jsonl): File-backed stores that live on the Vercel Lambda instance. These are cleared on cold starts and are suitable for demo and evaluation use. Not recommended for production record-keeping.
  • Supabase (production): Persistent PostgreSQL database with row-level security. Each organisation can only read its own records. See supabase/migrations/ for the full schema and RLS policies.

Audit log retention

The audit log (audit_log table) is append-only by design — RLS policies block DELETE and UPDATE operations on audit entries for all roles, including the record owner. This matches the DEA requirement for tamper-evident records under 21 CFR § 1304.04(f) (2-year minimum retention).

Ledger events themselves also use an append-only model — a "deletion" is recorded as a REVERSAL event with a corresponding negative quantity, preserving the full history.

Export and deletion requests

Exporting your data

  1. Navigate to /ledger/verify and click Export JSON — this downloads all ledger events with the full verification manifest.
  2. Navigate to /audit-log and click Export CSV or Export JSON for the complete audit trail.
  3. From /reconcile, download the monthly reconciliation PDF for each period.

Account deletion

To delete your account and all associated data, email hello@grantshelf.com with your organisation ID. We will purge all records within 30 days.

Regulatory hold: Audit log entries linked to DEA-required records must be retained for a minimum of 2 years under 21 CFR § 1304.04(f). If a deletion request covers records within the 2-year retention window, we will notify you of the applicable hold before proceeding.

Third-party data sharing

  • PostHog: Anonymised product analytics — page views and feature usage only. No email, no PII, no event content is sent to PostHog. You can opt out by blocking app.posthog.com in your network policy.
  • Stripe:Payment processing under Stripe's Privacy Policy. VetRx Ledger never sees raw card numbers. Stripe stores your billing email and subscription status; we store your Stripe Customer ID.
  • Vercel: Hosting and edge compute. Request metadata (IP, headers, response times) is retained by Vercel per their data processing agreement.
  • No data brokers. No ad networks. No data sales. VetRx Ledger does not sell, share, or transfer your data to any third party beyond the three service providers above.