Appearance
MongoDB Permissioning
🔴 LIVE STATE → see Atlas Access Matrix (auto-generated from
infra/atlas/access-matrix.ts, CI-guarded for accuracy). The matrix is the canonical source of truth for every user, role, env-var, and code consumer. THIS file remains as the original Phase 5 design plan; some details have evolved since (ENG-239 narrowedapp_read_staging, ENG-271 addedapp_read_local_staging, PR91 addedapp_writer_hubspot_sync, ENG-266 addedapp_admin_schema). Read the matrix first.
Status: Staging provisioned 2026-04-17. Prod rollout deferred until AWS cutover is verified. Related issue: #56
Execution artifacts:
- Atlas Access Matrix — the live source of truth (start here)
- ADR 0003 — Narrow-scoped MongoDB users — design rationale as accepted.
- Runbook — Atlas user provisioning — exact commands to reproduce against prod.
- Session log 2026-04-17 — what shipped on staging.
Current state
Today (.env.local):
MONGODB_URI→app-readuser, read-only access to theaskflorencedatabase. OK as-is.MONGODB_WRITE_URI→app-writeuser, readWrite for the whole DB. Overly broad. Violates least privilege. Currently used by every ingest script and anything that writes.
app-write is a dev convenience. It must be deleted before production launch. That deletion is a required exit criterion on Issue #56.
Target state (narrowly-scoped users)
| User | Role | Scope | Use | Status |
|---|---|---|---|---|
app-read (keep) | read | database askflorence | Public read queries (plans, zips, counties) | Exists |
app-write (dev only, delete before prod) | readWrite | database askflorence (too broad) | Dev convenience | Exists — must delete before prod |
app_writer_plans | readWrite | collections plans, zip_county, regions only | Ingest scripts going forward | NEW — create |
app_writer_survey | readWrite | collection agent_survey_responses only | Phase 2 survey writes from Vercel | NEW — P1, blocks Phase 2 |
app_writer_agents | readWrite | collections agents, agencies, agent_sessions, agent_audit_log, admins | Phase 5 agent portal writes | NEW — blocks Phase 5 |
app_admin_agents | readWrite + admin on agent collections | Same as app_writer_agents | Admin dashboard writes | NEW — blocks Phase 5 |
audit_reader | read | agent_audit_log only | Compliance queries, read-only | NEW — blocks first audit |
Why this matters for EDE / SOC 2
- Principle of least privilege. A compromised API route that handles plan search can't modify agent records. A compromised agent-facing route can't grant admin rights. This maps to SOC 2 CC6.3 and HIPAA § 164.312(a)(1).
- Audit clarity. MongoDB Atlas audit logs show which DB user performed which write. In SOC 2 controls review, that trail is evidence.
- Blast radius. If an LLM-generated query or user-input sanitization gap lets an attacker do something unexpected, the damage is bounded by the user's collection scope.
Env var mapping (target)
Stored in Vercel env today. Moves to AWS Secrets Manager post-migration (Phase 4).
MONGODB_URI_READ = app-read
MONGODB_URI_PLANS_WRITE = app_writer_plans (replaces MONGODB_WRITE_URI in scripts/db/*)
MONGODB_URI_SURVEY_WRITE = app_writer_survey
MONGODB_URI_AGENTS_WRITE = app_writer_agents
MONGODB_URI_AGENTS_ADMIN = app_admin_agents
MONGODB_URI_AUDIT_READ = audit_readerMigration order (ops task)
Sequenced so we never have to migrate everything at once:
- Create
app_writer_surveyahead of Phase 2. Phase 2 uses this exclusively — never touchesapp-write. - Create
app_writer_plansbefore Phase 5. Migratescripts/db/*.jsto use it.app-writecontinues to exist as an emergency fallback during dev. - Create
app_writer_agents,app_admin_agents,audit_readerbefore Phase 5 kicks off. - Before production launch:
- Audit all code (
scripts/db/*.jsandsrc/**/*.{ts,tsx}) forapp-write/MONGODB_WRITE_URIreferences. - Migrate any remaining callers to narrowly-scoped users.
- Delete
app-writefrom MongoDB Atlas. Verify no code path still depends on it.
- Audit all code (
Step 4 is a hard blocker on Issue #56 closing.
Atlas implementation notes
- Create users under Database Access in Atlas UI with scoped role grants.
- Use
readWriteat collection level, not database level, for allapp_writer_*users. Atlas supports this per docs. - Network access list stays as-is (Vercel egress IPs + team member IPs); no change needed per user.
- Do not give any
app_writer_*useradminrole. Onlyapp_admin_agentsgetsadminon its collections, and only super-admin-triggered actions use it. - Rotate connection strings annually at minimum. Audit log checks should alert on usage from unexpected source IPs.
Related
- Overview — how this fits into the phase roadmap
- Data Models — what each user writes to
- Compliance Model — the SOC 2 / HIPAA / EDE reasoning
- Issue #56 — the tracked task