Loading...
Loading...
Custom adoption case management platform shipped to production in 33 days. Field-level KMS encryption with HMAC blind-index dedup, polymorphic multi-entity schema, 8 event-driven and scheduled automations, full audit log on every sensitive-data access. Replaces the legacy system that ran 98% of adoptions in Kentucky and Tennessee.

Atlas is a custom adoption case management platform I built from scratch for Adoption Assistance, a Kentucky/Tennessee adoption agency. They had been running on InReach SAM, a legacy adoption case management system that handled the workflow but was old, fragile, and hostile to staff. Adoption Assistance handles about 98% of adoptions in Kentucky and Tennessee, so the system needed to actually fit how they work: applicant families, birth parents, and children on different tracks that converge at the match event, documents that attach to any of those entities, sensitive data handled with the encryption a healthcare-adjacent agency actually needs, and automation that fires on real events instead of running through another manual checklist. Atlas is live at app.adoptionassistance.com, imported 6,700+ legacy records and over 500,000 activity log entries idempotently, and gets shipped to almost every day.
The security story is the part I'm most proud of. Sensitive fields like SSN and TOTP secrets are encrypted at rest with AWS KMS envelope encryption, stored with a kms:v1: prefix so the migration is idempotent and the system gracefully tolerates legacy plaintext during the cutover. SSN dedup uses an HMAC-SHA256 blind index, so the system can find duplicate families by SSN without the database ever holding a decryptable value. Every sensitive-field reveal writes to an audit log that captures user, field, record, IP, timestamp, and whether the access was via impersonation. The audit log is fail-soft so a flaky write can't block a legitimate access. Authentication is JWT-based with a 4-hour sliding idle timeout (active users never get logged out mid-task), TOTP 2FA via any authenticator app (no SMS, no vendor lock-in), 10 recovery codes formatted in Crockford base32, and 7-day trusted devices that get instantly invalidated if an admin resets 2FA. Even the constant-time password comparison runs bcrypt against a fake hash on missing users to prevent email-enumeration timing attacks.
The case management UI is built around a stage strip: 8 stages from Inquiry to Finalized, with a one-click Advance button and a Change dropdown for off-path transitions (postponed, withdrawal, reopen). Advancing to Matched fires the match-packet automation, which gates on a master kill switch, the family's auto-email flag, a 90-day recency check, and a one-send-per-case dedup, then emails the family their match packet with replies routed to the coordinator handling that case. Eight automations total. Five are event-driven (match packet send, home-study expiration auto-fill based on case type, post-adoption report schedule generation on independent case creation, stage auto-advance on arrived-home, caseworker notification on arrival) and three are cron-driven (home-study expiration alerts in a -30 to +60 day window, post-adoption-report reminders, background-check expiration alerts). Every alert send writes to an audit table so staff can see exactly what fired, when, to whom, and whether it succeeded. There is a master kill switch on the entire automated send pipeline and a test-mode override that redirects all outbound mail to a single test inbox with the original recipients prepended to the subject.
The data model is comprehensive. A family record has 150+ fields covering two applicants (employment, income, criminal and mental health history, household composition, address history, contact preferences), and the schema includes detailed children records (placement history, special needs, original state/country, adopted names), birth parent records with a status workflow and per-case overlays for due date and paternity, and 40+ field case records with stage tracking and post-adoption report scheduling. Documents, notes, activity, and alerts all use polymorphic attachments so a single staff log surfaces wherever it's relevant. The document library has versioning, consent-governed publishing (images are tagged with consent level so only the right ones can appear on the agency's public site), and bulk ZIP export via archiver streamed through presigned S3 URLs to bypass Vercel's body cap.
The codebase is currently single-tenant but designed for productization. Tenant name, logo, domain, and contact info are all env vars. The Atlas tables live in their own Postgres schema, isolated from the legacy Payload tables. S3 keys are scoped by organization. When the next adoption agency materializes (Julie, the owner, knows other agencies that might be interested) it's a refactoring problem (add an org_id to the permission model and filter queries) not a rewrite.
Adoption Assistance had been running on InReach SAM, a legacy adoption case management system, for years. It handled the workflow but was old, fragile, and hostile to staff. They handle about 98% of adoptions in Kentucky and Tennessee, with real compliance requirements and sensitive data across thousands of families and birth parents. Off-the-shelf case management tools don't fit how adoption work actually unfolds: applicant families and birth parents on parallel tracks that converge at a match, children on their own placement timeline, documents that attach to any of those entities, mandatory state-specific reporting cycles, and the kind of encryption a healthcare-adjacent agency actually needs.
Built Atlas from scratch in 33 days as a custom Next.js 15 + Drizzle + Postgres platform with a multi-entity polymorphic schema. Documents, notes, activity, and alerts all attach to families, cases, children, or birth parents without duplication. Field-level KMS envelope encryption for SSN and TOTP secrets, with HMAC blind indices for dedup-without-decryption. JWT auth with 4-hour sliding idle, TOTP 2FA, and per-field audit logging on every sensitive reveal. Eight automation triggers (event-driven and cron) handle match-packet sends, home-study expiration alerts, post-adoption report scheduling, and stage auto-progression. Migrated 6,700+ legacy SAM records idempotently with trace-back fields for safe rollback.
Live in production at app.adoptionassistance.com. Migrated 6,700+ legacy SAM records (families, cases, children, birth parents, 500K+ activity log entries) idempotently. Replaces the legacy system that ran 98% of adoptions in Kentucky and Tennessee. Designed for productization (env-var tenancy, schema isolation, consent governance) so other adoption agencies can deploy on the same codebase when the demand materializes.
Next.js 15 App Router on Vercel for the application layer. Postgres on Supabase via Drizzle ORM, with Atlas tables living in their own schema (kept isolated from the legacy Payload tables so the migration could happen without contaminating production data). JWT auth handled in middleware, with a 4-hour sliding idle re-signing the token on every request so active users stay logged in. AWS KMS for envelope-encrypting sensitive fields at the column level (SSN, TOTP secrets) with HMAC-SHA256 blind indices for dedup. AWS S3 for document storage with SSE-KMS, presigned URLs for uploads to bypass Vercel's body cap. AWS SES for transactional and family-facing email, with a global suppression list auto-updated from SES bounces and a master kill switch on automated sends. Cron-driven automations fire daily via a scheduled HTTP endpoint that iterates the registered alert types. The codebase enforces tenancy via env vars so adding a second adoption agency is a refactoring problem (add org_id to users, filter queries) not a rewrite.

Check out some of my other work