Appearance
Compliance Model
Status: Design locked. Implementation happens across Phase 2-6. EDE audit readiness is the design target from day one.
Why we're building this way
CMS EDE (Enhanced Direct Enrollment) Phase 3 API integration is on our roadmap for later in 2026. EDE auditors don't just check how you're configured on audit day — they examine how you've been operating for months. The same is true for SOC 2 Type 2: the audit period is 3-12 months of operations, not a snapshot.
Design principle: build the agent portal on compliant foundations from the first line of code. Operate at EDE standards from day one so the audit trail is clean.
This is the reason the full agent portal (auth, NIPR, ID verify, dashboards, admin) does not ship on current Vercel infrastructure. It waits for AWS migration (Issue #47) so the infrastructure itself passes audit.
Standards we're targeting
| Standard | Who requires it | Relevant for |
|---|---|---|
| CMS EDE Phase 3 + Appendix A | CMS — required for agents to submit member enrollments via our API | Agent auth, session, audit |
| SOC 2 Type 2 | Carriers and enterprise partners | Access control, change management, monitoring |
| HIPAA Security Rule | Any system touching PHI | Encryption, audit log, access control |
| GDPR / CCPA | Users in EU / California jurisdictions | Consent capture, data access rights, deletion |
| CAN-SPAM | U.S. commercial email | Unsubscribe link, opt-in proof |
| NIST 800-63B AAL2 | Authentication assurance for healthcare data | Multi-factor authentication design |
Control-to-implementation mapping
Access control (SOC 2 CC6.3, HIPAA § 164.312(a)(1))
| Control | How we satisfy it |
|---|---|
| Unique user identification | NPN is the immutable primary key; email is unique, verifiable, mutable with flow |
| Automatic logoff | 15-min idle / 8-hr max session (both tiers) |
| Encryption of ePHI at rest | MongoDB Atlas encryption at rest (default on M10+), AWS S3 server-side encryption |
| Encryption of ePHI in transit | TLS 1.3 only. No HTTP. CloudFront + ACM certs. Internal service-to-service via VPC + mTLS. |
| Role-based access | admins collection with super_admin / admin / support roles, enforced in middleware |
| Least privilege on data | Per-collection MongoDB users (see MongoDB Permissioning) |
Audit controls (SOC 2 CC7.2, HIPAA § 164.312(b))
Every auth event, admin action, and data change writes to agent_audit_log:
- Actor (agent / admin / super-admin / system), actor ID, actor email
- Action name (
login_succeeded,status_changed,email_change_confirmed, etc.) - Resource type and ID
- Before/after diff for mutations
- IP, userAgent, timestamp
- Outcome (success / failure)
Retention: 6 years minimum (HIPAA § 164.316), 10 years preferred for EDE safety margin. Append-only. Immutable — no update or delete operations permitted on this collection.
A dedicated audit_reader MongoDB user is the only identity that can read the audit log via API. Compliance queries go through a controlled export script, not ad-hoc queries.
Multi-factor authentication (NIST 800-63B AAL2, EDE Appendix A § 9)
True multi-factor requires two different factor types. Our Tier 2 design:
- Primary: Email magic link (something you have)
- Second: TOTP via authenticator app (something you have, different device class)
- Fallback: 10 single-use recovery codes (something you have, out-of-band storage)
- Session: 15 min idle, 8 hr max
- Failed attempt rate limit: 5/email/hour with exponential backoff
SMS deprecated per NIST 800-63B rev 3. Not used.
Super-admin adds password (something you know) and IP allowlist (somewhere you are) on top of TOTP — three factor types, AAL3-adjacent.
See Auth Architecture for details.
Consent capture (GDPR, CCPA, HubSpot/Salesforce import requirements)
Every email-capturing record stores a consent sub-document with:
versions.privacyPolicy— version tag of the policy in effect when consent was givenversions.termsOfService— sameversions.consentStatement— exact text shown to the user at capture timeoptIns.platformContact— "contact me about becoming an agent"optIns.marketingEmail— "product updates and research"optIns.marketingSms— future-reservedcapturedAt— server timestampip— request IPuserAgent— request userAgent stringreferrer,pageUrl— where the user came from and captured onmethod—"checkbox"(explicit opt-in),"implicit"(service operation),"verbal_recorded"(future: phone consent)
This structure makes any future CRM migration (HubSpot, Salesforce) provable by construction. The CRM's import flow demands this exact evidence; we have it.
Versioned privacy pages at /privacy?v=2026.04 and /terms?v=2026.04 keep a frozen copy of every version users ever agreed to. See Issue #58.
Unsubscribe flow (CAN-SPAM)
Every marketing email includes an unsubscribe link: /unsubscribe?token=<hmac>. Token is an HMAC-signed payload { email, optInTypes, issuedAt }. Tokens do not expire (CAN-SPAM requires indefinite effectiveness). On click:
- Verify HMAC.
- Flip
consent.optIns.marketingEmail = falseon matching records. - Write audit log event.
- Show confirmation with re-subscribe option.
See Issue #59.
Vendor BAA coverage
Every vendor that could touch PHI at any point must have a signed HIPAA Business Associate Agreement. Tracked in Issue #57, owned by Asad.
| Vendor | Service | Data | BAA status | Action |
|---|---|---|---|---|
| MongoDB Atlas | Primary DB | All app data incl. future PHI | TBC | Confirm M10 plan includes BAA |
| Resend | Transactional email | Agent emails, future PHI-adjacent | TBC | Confirm; if none, plan migration to Postmark / AWS SES / Mailgun |
| AWS | Post-migration infra | Everything | Likely covered | Confirm AWS BAA covers ECS/Fargate, S3, Secrets Manager, CloudFront, CloudWatch, CloudTrail |
| NIPR | NPN validation (Phase 5) | Agent PII | TBC | Required before first PDB call |
| ID verification vendor | Phase 5, vendor TBD | Government ID, selfie | N/A | BAA must be a selection criterion |
| Cloudflare / Turnstile | Bot protection | IP, userAgent | TBC | Confirm; usually no PII seen |
| PostHog | Analytics | Potentially PII/PHI | TBC | HIPAA-compliant tier or don't log PHI |
| Vercel | Pre-migration hosting | No PHI ever | Skip | Migrating off (Issue #47); no PHI on this path |
NIPR NPN validation (Phase 5)
Agents submit an NPN at onboarding. Server validates format (6-10 digits), then calls NIPR's PDB Detail Report API to confirm the NPN is real, active, and the agent is licensed in the states they claim.
- Cost: $1.30 per PDB Detail Report call at onboarding. $0.25/target/month for ongoing alert monitoring on active agents.
- Storage: response stored in
agent_nipr_recordsfor audit trail. - Flow:
- Agent submits onboarding.
- Server validates format, calls NIPR.
- If "not found" / "inactive" → reject with clear messaging.
- If licensed states don't include declared states → flag for manual review.
- On success → proceed to ID verification step.
- Monthly alerts: paid subscription catches license expiration or discipline actions on actives.
ID verification (Phase 5)
KYC for licensed professionals who'll handle member PII. EDE audit will ask how we verified the identity of every person with access.
Vendor deferred (decision before Phase 4 kickoff). Candidates: Persona, Stripe Identity, Plaid, Veriff. BAA required.
Flow: government ID photo + selfie + liveness check. Optional: match ID name against NPN record name. Verification status stored in agent_id_verifications. Agent can reach Tier 1 before ID verification completes but cannot be activated until it does.
Adapter pattern in src/lib/agent-identity.ts keeps vendor swap as a localized change.
Defense in depth for admin management
Admin role changes are a high-value target. Multiple layers gate them:
- Network layer: CloudFront/WAF rejects
/sa-*routes from non-allowlisted IPs with 404 before the app sees the request. - Auth layer: super-admin requires password + TOTP. Other admins cannot reach super-admin routes.
- Route gating:
/super-admin/*returns 404 unless session is super-admin. Non-super-admins never see the routes exist. - Fresh TOTP: mutation endpoints require TOTP completed within the last 5 minutes. Stale session → re-prompt.
- MongoDB least privilege: only
app_admin_agentscan write toadmins. Agent-facing users cannot. - Audit log: every mutation writes before/after diff, actor, IP, userAgent.
- Confirmation: UI requires typing the target email before submit. No accidental grants.
- Rate limits: max 5 admin mutations per super-admin per hour; alerts on burst.
- No self-elevation: super-admin can't modify their own row via UI; another super-admin must do it.
- Notifications: every admin mutation emails all super-admins. Unauthorized changes get seen immediately.
- Break-glass scripts for bootstrap and DR. Each script writes audit log automatically.
Sub-issues tracking compliance work
| Issue | What | Priority |
|---|---|---|
| #55 | Draft /privacy + /terms pages | P1 — blocks Phase 2 |
| #56 | MongoDB permissioned users | P1 for survey writer |
| #57 | Vendor HIPAA BAAs | P1 — owner: Asad |
| #58 | Consent + policy versioning | P2 |
| #59 | Unsubscribe flow (CAN-SPAM) | P2 |
| #47 | AWS migration | Blocks Phase 5+ |
Related
- Overview — phase roadmap and shipping sequence
- Auth Architecture — Tier 1 / Tier 2 / super-admin design
- MongoDB Permissioning — least-privilege DB user scopes
- Data Models — the collections and retention policies