Read This First

Settle — Prerequisites & Decision Register

Every infrastructure decision, with options and trade-offs, before any architecture document is written. Read this, set up your accounts, and run docker compose up — all in under 30 minutes.

Product Guided estate administration
Year 1 target 500 estates
Team size 1–2 developers
Budget ceiling <$100/mo at launch
Doc status v1.0 — 2026-04-03
Five Core Principles

1. Docker image that runs anywhere — over platform-specific configuration.
2. "Can Postgres do this?" — before adding any new service.
3. Library-based auth — over managed auth services.
4. Every vendor has a documented exit path — before it is adopted.
5. MVP dependency budget: maximum 5 external services — 6 if counting Lob for physical mail.


01

Minimum Viable Infrastructure

Settle has more infrastructure surface area than a typical SaaS application. Three capabilities force decisions that most apps defer indefinitely: column-level encryption for PII (SSNs, death certificates), physical mail dispatch for Tier 2 notifications, and a state-specific rules engine covering 50 US jurisdictions. This section strips each down to its minimal viable form before any vendor is added.

The Absolute Minimum

A container host running the application and a managed Postgres instance with pgcrypto enabled. That is the complete infrastructure core. Everything else is a feature decision, not an infrastructure requirement. The sections below argue for deferring each additional service until user feedback makes the deferral painful.

Bare Minimum Core
  • Container hostFly.io, Railway, or Render
  • Managed Postgrespgcrypto extension enabled
Add at Launch
  • Transactional emailResend — task reminders & auth
  • Error trackingSentry free tier
Defer Until Needed
  • Lob / physical mailUntil Tier 2 ships
  • Object storageUntil >5,000 docs
  • Benefit scanner APIsUntil manual checklist is painful
  • RedisUntil pg-boss is insufficient

Questioning Each Potential Service

The following table forces a deferral decision for every service that is commonly added "just in case." The decision is recorded here so it does not have to be re-litigated during architecture review.

Service Common assumption MVP reality Defer trigger
Lob (physical mail) Required for Tier 2 notifications Generate a formatted PDF. Executor prints and mails it. Zero API cost. User feedback: print-and-mail is causing abandonment
Object storage (R2/S3) Needed for document uploads 500 estates × 5 docs = 2,500 docs. Postgres large objects handle this comfortably. >5,000 documents or >5 GB in Postgres
Benefit scanner APIs Core feature: automated discovery Manual checklist: "Check these 5 sites for unclaimed property." Humans are fine at MVP scale. Manual checklist creates measurable support load
Redis Needed for sessions & queues Sessions in Postgres. Job queue via pg-boss. Redis adds zero value before high concurrency. pg-boss throughput becomes a bottleneck (>1,000 jobs/min)
Dedicated rules engine 50 states requires Drools or json-rules-engine State rules are if/then thresholds and form names. JSONB rows in Postgres are queryable, versionable, and require zero new dependencies. Rule complexity exceeds what can be expressed in structured JSON
On Tier 1 Notifications at MVP

Tier 1 (subscription cancellation calls) requires an email or phone integration. Consider whether Tier 1 is MVP scope at all. Starting with Tier 3 only — generate a script, the executor makes the call — defers all outbound communication infrastructure except task-reminder email. Tier 1 can ship in Month 2.


02

Decision Register

Each decision below includes the options considered, a comparison of trade-offs, and a recorded recommendation. Decisions marked Decided are final for MVP. Decisions marked Deferred are explicitly not being made yet. Revisit deferred decisions when their stated trigger is reached.

D-01 Hosting Platform Decided

Settle requires a background worker process for notification scheduling alongside the web process. The hosting platform must run Docker images and support multiple process types without a significant pricing step-change at MVP scale.

Option Workers Postgres included Free tier Migration effort
Railway Separate services per process Yes (Railway Postgres) $5 credit/mo Change CI/CD target. ~1 day.
Render Background Workers service type Yes (Render Postgres) Free web service (spins down) Change CI/CD target. ~1 day.
Cloud Run (GCP) Cloud Tasks / separate service No (Cloud SQL separate) Generous free tier GCP-specific config. ~1 week.
Recommendation
Fly.io. Scale-to-zero worker Machines are ideal for Settle's notification processing model: workers are idle most of the day and spike briefly when a batch runs. The included managed Postgres eliminates one account. Migration to any other Docker host is a single CI/CD file change.
D-02 Database Decided

Postgres with the pgcrypto extension is non-negotiable. Column-level encryption for SSNs and sensitive PII must live in the application layer, not a third-party vault, at MVP budget. The only open question is which managed Postgres provider to use.

Non-Negotiable Constraint

Postgres + pgcrypto is the encryption strategy. Any managed Postgres provider supports extensions, including pgcrypto. Do not choose a provider that restricts extensions (e.g., some PlanetScale configurations). Verify pgcrypto is available before provisioning.

Provider pgcrypto Long-lived connections Notes
Railway Postgres Yes Yes Reasonable if using Railway for hosting
Neon Yes Serverless (cold starts) Generous free tier; cold starts matter for background workers
Supabase Postgres Yes Yes Row-level security is bonus; pauses on free tier
Recommendation
Use the Postgres instance included with whichever hosting provider is chosen for the application. One fewer account, co-located with the app for latency, and the same migration path as the host. Neon's serverless cold starts are a concern for background workers processing 16–18 month workflows.
D-03 Authentication Decided

Settle's user base is primarily grieving family members, many of whom are not technically sophisticated and may be elderly. Authentication must support email/password as the primary flow, magic links for users who lose passwords, and an invite-based access model so executors can grant view-only access to other family members.

Option Type Magic link Invite flow Vendor lock-in
Lucia Library Manual implementation Manual implementation None
Auth.js (NextAuth) Library Yes (email provider) No built-in None; Next.js-centric DX
Clerk Managed service Yes Yes (Organizations) High — data lives in Clerk
Recommendation
BetterAuth. It is a library (not a service), stores sessions in the application Postgres instance, and ships built-in support for email/password, magic links, and the organizations pattern needed for the executor-invites-family-member flow. No vendor lock-in — migrating means rewriting session middleware, not exporting user data from a third party.
D-04 Frontend Framework Decided

SvelteKit has been selected. Brief rationale for the record:

  • Server-side rendering and API routes in a single project eliminates a separate API server at MVP scale.
  • Form actions provide progressive enhancement critical for a user base that may be on slow mobile connections.
  • Bundle sizes are smaller than React-based alternatives — relevant for older devices commonly used by the target demographic.
  • Compiler-based reactivity avoids the cognitive overhead of hooks for a solo or two-person team.
Mobile App Consideration

If a native mobile app is a Year 2 requirement, this decision should be revisited. SvelteKit does not share code with React Native or Flutter. A mobile app would be a separate project, or the frontend choice changes to React (web + React Native code sharing). See Open Questions, item Q-04.

D-05 State-Specific Rules Engine Decided

This is an application decision, not an infrastructure decision. The 50-state rules do not require a dedicated rules engine service. They are structured data: thresholds (probate limits), form identifiers, required notices, and timelines. All of this is expressible as JSONB rows in Postgres.

Option Storage Updates Queryable New dependency
YAML/JSON files in repo Git-versioned files Deploy on every change No — application-level only None
Dedicated rules engine Engine-specific format Engine API Engine-specific High — new service + DSL
Recommendation
JSONB rows in Postgres. A state_rules table with a state_code column and a rules JSONB column covers every current requirement. Rules can be queried in SQL, updated without a deploy, and versioned with a valid_from/valid_until timestamp pair for legal effective-date tracking. A dedicated rules engine adds a DSL to learn and a service to operate for if/then logic that Postgres handles natively.
D-06 Physical Mail (Tier 2 Notifications) Deferred

Physical mail sending (certified letters to creditors, agencies, financial institutions) is a Tier 2 notification feature. It is not MVP. At MVP, Settle generates a correctly formatted letter as a PDF — with all addresses, legal language, and state-specific boilerplate — and the executor prints and mails it. This is explicitly presented to users as intentional, not a gap.

Cost Reality Check Before Adopting Lob

Lob charges approximately $1.50 per letter. An estate generating 10 letters × 2,000 estates/year = $30,000/year in direct mail costs. This must be priced into per-estate subscription tiers before the integration is built, not after. Do not build Lob integration until pricing is validated and the cost is explicitly passed through.

Option Cost / letter API quality Exit path
Lob ~$1.50 Excellent PostGrid, DIY print vendor
PostGrid ~$1.40 Good Lob, DIY print vendor
Click2Mail ~$1.20 Fair Limited API; harder to migrate
Deferral Trigger
Add Lob (or PostGrid) when user feedback shows that print-and-mail creates measurable abandonment or support tickets. Validate the $30K/year cost is covered by subscription pricing before building. When building, abstract behind an IMailDispatcher interface so the vendor can be swapped.
D-07 Object Storage (Document Uploads) Deferred

Death certificates, wills, financial statements, and account closure confirmations need to be stored. At MVP scale, Postgres large objects (or bytea columns) are the correct answer. 500 estates × ~5 documents × ~500KB average = ~1.25 GB. This fits comfortably in any managed Postgres plan and avoids an additional vendor relationship.

Deferral Trigger
Move documents to Cloudflare R2 (preferred for egress pricing) or S3 when total document storage exceeds 5 GB or total document count exceeds 5,000, whichever comes first. The application code should abstract storage behind a repository interface from day one so the migration is a configuration change, not a refactor.
D-08 Background Job Queue Decided

Settle's workflow engine processes 38-task dependency chains that run for 16–18 months per estate. Notification batches need scheduling. Neither of these requirements demands a separate queue service at MVP scale.

OptionInfrastructureDurableScheduled jobs
BullMQ Redis required Yes Yes
Inngest Managed service Yes Yes
Temporal Self-hosted or Cloud Yes Yes
Recommendation
pg-boss. Runs entirely in Postgres with no additional infrastructure. Job state is transactional — a job is either committed or not, with no possibility of a Redis outage causing lost notifications. Sufficient throughput for years at Settle's projected scale. Revisit if job volume exceeds ~1,000 jobs/minute sustained.
D-09 Transactional Email Decided

Required at MVP for: magic link auth, task reminders, and estate status notifications. The integration surface is intentionally minimal — a single HTTP POST per email.

OptionFree tierAPIExit path
Postmark 100 emails/mo Simple HTTP REST Any SMTP provider
SendGrid 100 emails/day REST + SMTP Any SMTP provider
AWS SES 62,000/mo (in-region) AWS SDK AWS-specific; harder to migrate
Recommendation
Resend. The free tier covers MVP volume, the API is a single HTTP call, and the exit path is standard SMTP — any provider works. Abstract the email call behind a simple interface (send(to, subject, html)) so the provider can be swapped without touching application code.
D-10 Error Tracking & Observability Decided

Errors in estate workflows have real consequences for grieving families. A missed notification or a failed job is not a UX issue — it is a compliance risk. Error tracking is required from day one, not deferred.

Recommendation
Sentry free tier. 5,000 errors/month is sufficient for MVP. The SDK integrates in under an hour, captures stack traces with request context, and provides a dashboard. Do not build custom error logging before the product has users. Structured application logs (pino or similar) to stdout, captured by the hosting platform, cover the rest.
Observability Roadmap

At Year 2 scale (5,000 estates), consider adding Grafana Cloud (free tier) for metrics and dashboards. Postgres slow query logging and pg-boss job failure rates are the first metrics that matter. Do not add OpenTelemetry before you have a problem to diagnose.

D-11 Encryption Key Management Decided

This is the highest-stakes infrastructure decision in this register. Settle stores SSNs, dates of death, and financial account numbers. Column-level encryption via pgcrypto (pgp_sym_encrypt) requires a symmetric key. Where that key lives determines the blast radius of a database breach.

Critical Security Decision

A leaked encryption key means all encrypted columns in the database are readable in plaintext. This is not a recoverable event. Treat key management as the most sensitive operational decision Settle makes.

Option Complexity Breach blast radius Cost
AWS KMS Medium Low — key never leaves KMS ~$1/key/month
HashiCorp Vault High Low — key never leaves Vault Self-hosted or ~$0.03/hr
Age / application-managed Medium Medium — depends on key storage $0
Recommendation
Environment variable at MVP, stored in the hosting platform's secret management UI (Fly.io Secrets, Railway Variables). The key must be a cryptographically random 32-byte value, base64-encoded. It must never appear in source code, logs, or error traces. Rotate manually on a documented schedule (quarterly). Migrate to AWS KMS or equivalent before SOC 2 audit, or when annual revenue justifies the operational overhead. Document the migration path before the first SSN is written to the database.

Encryption Implementation Pattern

-- Column definition (never store plaintext SSN)
ALTER TABLE estate_contacts
  ADD COLUMN ssn_encrypted TEXT;

-- Write (application layer, key from env)
UPDATE estate_contacts
   SET ssn_encrypted = pgp_sym_encrypt($1, $2)
 WHERE id = $3;
-- $2 = process.env.DB_ENCRYPTION_KEY

-- Read (decrypt only in application, not in views)
SELECT pgp_sym_decrypt(ssn_encrypted::bytea, $1)
  FROM estate_contacts
 WHERE id = $2;

03

Portability Assessment

Every vendor in the stack must have a documented exit path before it is adopted. This table defines that path for each MVP service. "Lock-in level" measures how much application code changes if the vendor is replaced.

Service Current vendor Lock-in Exit path Migration effort
Hosting Fly.io Low Any Docker host (Railway, Render, Cloud Run) Update fly.toml → platform config. ~1 day.
Database Fly Postgres Low pg_dump → restore to any Postgres Dump, restore, update DATABASE_URL. ~2 hours.
Auth BetterAuth Low Replace session middleware; user data stays in app Postgres Rewrite auth routes + middleware. ~2 days.
Email Resend Low Any SMTP or HTTP email provider Swap 1 environment variable + 1 adapter file. ~1 hour.
Error tracking Sentry Low Datadog, Rollbar, self-hosted Sentry Replace SDK import + DSN env var. ~1 hour.
Job queue pg-boss Low BullMQ (+ Redis), Inngest, Temporal Replace queue adapter; job logic unchanged. ~3 days.
Physical mail PDF (deferred) Low Lob, PostGrid behind IMailDispatcher Implement interface. ~1 day.
Encryption keys Env variable Medium Re-encrypt all rows after migrating to KMS/Vault Decrypt all rows → re-encrypt with new key. ~1 day + downtime window.
Document storage Postgres (deferred) Low R2 or S3 behind storage repository interface Migrate blobs + swap interface implementation. ~2 days.

04

Accounts & CLI Tools Needed

This is the complete list for local development and the first deploy. MVP requires five accounts. The total time to create accounts and run docker compose up locally should be under 30 minutes.

MVP — Required Before First Commit

  • GitHub — source control, CI/CD via GitHub Actions Free. Create at github.com. Install GitHub CLI: brew install gh 3 min
  • Fly.io — hosting + managed Postgres (one account covers both) Free tier. Create at fly.io. Install CLI: brew install flyctl, then flyctl auth login 5 min
  • Resend — transactional email (magic links, reminders) Free tier: 3,000 emails/month. Create at resend.com. Copy API key to .env. 3 min
  • Sentry — error tracking and alerting Free tier: 5,000 errors/month. Create at sentry.io. Copy DSN to .env. 3 min
  • Domain registrar — DNS for settle.app or equivalent Cloudflare Registrar recommended (at-cost pricing, free DNS, proxy). ~$10/year for .app. 5 min

Local Development — Required Before Running the App

  • Docker Desktop — runs Postgres locally via compose Install at docker.com/products/docker-desktop 5 min
  • Node.js 20 LTS — SvelteKit runtime brew install node or use fnm / nvm for version management 2 min
  • Generate DB_ENCRYPTION_KEY — required before any SSN is written node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" — save to .env immediately. Never commit. 1 min
  • Copy .env.example to .env — fill in all values DATABASE_URL, RESEND_API_KEY, SENTRY_DSN, DB_ENCRYPTION_KEY, SESSION_SECRET 2 min

Deferred — Do Not Create Yet

  • Lob — physical mail API Deferred until Tier 2 notifications ship. ~$1.50/letter. Deferred
  • Cloudflare R2 — object storage Deferred until Postgres document storage exceeds 5 GB. Deferred
  • Redis / Upstash — job queue / cache Deferred until pg-boss throughput is a bottleneck. Deferred
  • AWS KMS or HashiCorp Vault — key management Deferred until SOC 2 audit or Year 2 scale. Deferred

Minimum .env Template

# Database
DATABASE_URL=postgresql://settle:settle@localhost:5432/settle

# Encryption — generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
DB_ENCRYPTION_KEY=<32-byte base64 string>

# Session
SESSION_SECRET=<random 64-char string>

# Email
RESEND_API_KEY=re_<your key>
EMAIL_FROM="Settle <notifications@yourdomain.com>"

# Error tracking
SENTRY_DSN=<your dsn>
NODE_ENV=development

05

Dependency Budget

The maximum external service count is 5 at MVP (6 counting Lob when it ships). This is a firm constraint, not a guideline. Every service added increases operational surface area for a solo or two-person team. Adding a service requires removing or justifying an existing one.

MVP (now)
4 of 5 max
+ Lob
5 of 5 max
Hard limit
6 (special case)
# Service Phase Justification
1 Fly.io (hosting + Postgres) MVP Non-negotiable — runs the app and stores all data
2 Resend MVP Auth magic links require email delivery
3 Sentry MVP Errors in estate workflows have compliance consequences
4 Cloudflare (DNS + domain) MVP Required for any public product
5 Lob Post-MVP Deferred — PDF generation suffices until user feedback says otherwise
Object storage, Redis, KMS, benefit APIs Year 2+ Each has a documented trigger condition; none are needed at MVP

06

Cost Projection

Infrastructure costs at three stages. The goal is $0–25/month at MVP with generous free tiers carrying the load. The first significant cost step is Lob when Tier 2 ships — that cost must be priced into subscription tiers, not absorbed.

Service Free tier MVP (<500 estates) Year 2 (5,000 estates) Year 3 (50,000 estates)
Fly.io (app + worker) 3 shared VMs ~$0 ~$20/mo ~$150/mo
Fly Postgres Included ~$0 ~$15/mo ~$60/mo
Resend 3,000 emails/mo ~$0 ~$20/mo ~$90/mo
Sentry 5,000 errors/mo ~$0 ~$0 ~$26/mo
Cloudflare (DNS + domain) Free DNS ~$10/yr ~$10/yr ~$10/yr
Lob (physical mail) post-MVP None $0 (deferred) ~$2,500/mo * ~$25,000/mo *
Object storage (R2) Year 2 10 GB free $0 (deferred) ~$0 ~$15/mo
Infrastructure total ~$1/mo ~$55/mo + Lob pass-through ~$340/mo + Lob pass-through
* Lob Cost Is a Pass-Through, Not an Overhead

At 5,000 estates/year × 10 letters/estate × $1.50 = $75,000/year in Lob costs. This cannot be absorbed as infrastructure overhead. It must be priced explicitly: either as a per-estate fee or as a Tier 2 add-on. Validate this pricing model before the Lob integration is built. If per-letter cost is unacceptable, evaluate PostGrid ($1.40) or a co-mailing arrangement at volume.


07

Open Questions

These questions are explicitly not answered yet. Each has a stated trigger — the event that forces the decision. Do not answer them early. Do not let them block MVP development.