Tideswell vs Terra
Terra is a modern reimagining of Tideswell, built with lessons learned from years of operating government intake infrastructure.Terra isn’t a fork of Tideswell—it’s a clean-slate rebuild that achieves feature parity while fundamentally rethinking the architecture. This page explains the relationship, the differences, and why we made the choices we did.
The Relationship
Tideswell was the original government intake platform, built to handle benefit applications at scale. It served its purpose well, processing hundreds of thousands of applications. But like all software, it accumulated technical debt. Terra is the successor. Same mission, new architecture. We took what worked in Tideswell, fixed what didn’t, and rebuilt from first principles with modern tools.Feature Parity Status
Terra has achieved full feature parity with Tideswell. Here’s how the features map:Core Intake Features
| Feature | Tideswell | Terra | Notes |
|---|---|---|---|
| Form builder | Drag-drop | Drag-drop | Terra uses dnd-kit, modern UX |
| Conditional logic | JSON rules | JSON conditions | Same power, cleaner syntax |
| Multi-language | ✓ | ✓ | Terra has auto-translation |
| File uploads | S3 | Supabase + Drive | Terra adds Google Drive option |
| Submissions | MongoDB | PostgreSQL | Terra adds encryption at rest |
| Status tracking | ✓ | ✓ | Same statuses |
| Applicant portal | ✓ | ✓ | Terra rebuilt with RSC |
Form Lifecycle
| Feature | Tideswell | Terra | Notes |
|---|---|---|---|
| Draft/Published versions | ✓ | ✓ | Terra stores both schemas |
| Scheduled publishing | autoPublishAt | published_at | Same feature, clearer naming |
| Scheduled closing | autoCloseAt | closes_at | Same feature, clearer naming |
| Form freezing | frozen flag | frozen flag | Prevents edits after first submission |
| Outcome field | ✓ | ✓ | Applicant-visible result text |
Identity & Verification
| Feature | Tideswell | Terra | Notes |
|---|---|---|---|
| ID verification | Plaid | Plaid | Same integration |
| Bank verification | Plaid | Plaid | Same integration |
| Address lookup | Smarty | Smarty | Same integration |
| Anonymous forms | ✓ | ✓ | is_anonymous flag |
| Applicant identity | Account model | applicants table | Terra normalizes across forms |
Integrations
| Feature | Tideswell | Terra | Notes |
|---|---|---|---|
| Webhooks | Custom triggers | Webhook configs | Terra adds event history |
| Airtable sync | ✓ | ✓ | Terra adds connection testing |
| Email notifications | Custom | Resend | Terra uses React Email templates |
| SMS notifications | Twilio | Twilio | Same integration |
| Google Drive | ✓ | ✓ | File uploads to shared drives |
Compliance & Audit
| Feature | Tideswell | Terra | Notes |
|---|---|---|---|
| Audit logging | MongoDB events | audit_logs table | Terra is more comprehensive |
| Geolocation capture | IP + Cloudflare | IP + Vercel/CF | Same data, anonymized |
| PII encryption | Partial | Field-level | Terra encrypts at submission |
| Data retention | Manual | Soft delete + cleanup | Terra has automated cleanup |
Architectural Differences
Database: MongoDB → PostgreSQL
Why we changed:| MongoDB (Tideswell) | PostgreSQL (Terra) |
|---|---|
| Flexible schema | Flexible via JSONB |
| Document queries | SQL + JSONB operators |
| Manual relations | Foreign keys |
| GridFS for files | Supabase Storage |
| Mongoose ODM | Raw SQL + DAL |
- ACID transactions for multi-step operations
- Foreign key constraints that prevent orphaned records
- Row Level Security at the database layer
- JSONB for schema flexibility without sacrificing structure
Auth: Custom → WorkOS
Why we changed: Tideswell rolled its own authentication. It worked, but required ongoing maintenance for security patches, session management, and OAuth providers. Terra uses WorkOS because:- SSO (SAML, OIDC) is built-in for enterprise clients
- MFA is handled by the provider
- Security patches are their responsibility
- We focus on intake, not auth
Queue: File + Memory → Database
Why we changed: Tideswell used a file-based queue with in-memory fallback. This caused problems:- Jobs lost on server restart
- No visibility into queue state
- Hard to debug failed operations
- Durable: Jobs survive restarts
- Visible: Query the table to see queue health
- Debuggable: Failed jobs have error messages
- Scalable: Multiple workers can claim jobs
Frontend: Express + EJS → Next.js + RSC
Why we changed:| Express/EJS (Tideswell) | Next.js/RSC (Terra) |
|---|---|
| Server-rendered HTML | Server Components |
| jQuery for interactivity | React for interactivity |
| Manual bundling | Automatic code splitting |
| API routes for data | Server Actions |
| Separate admin app | Unified app |
- Type safety across client/server boundary
- Automatic code splitting per route
- Streaming for large pages
- Server Actions that are just function calls
What We Kept
Not everything changed. These patterns worked well in Tideswell and survived into Terra:JSON Form Schema
Both platforms store form definitions as JSON. The schema structure is similar:Async-First Submission Flow
Both platforms follow the same pattern:- Validate and save submission (sync)
- Queue integrations (async)
- Process queue (background worker)
Multi-Language as Core
Both platforms treat translations as first-class citizens. Every label, error message, and piece of content supports multiple languages through theI18nString pattern.
Audit Everything
Both platforms log every significant action. The implementation differs (MongoDB events vs PostgreSQL table), but the philosophy is identical.Migration Path
Moving from Tideswell to Terra isn’t automatic—the data models are different enough that a migration script is required. Key mappings:| Tideswell | Terra |
|---|---|
Form document | forms row + JSONB schema |
Submission document | submissions row + encrypted JSONB data |
Account document | applicants row |
WebhookTrigger document | webhook_configs row |
WebhookEvent document | webhook_events row |
Question embedded | Element in schema JSONB |
Why Not Fork?
We could have forked Tideswell and incrementally improved it. We didn’t because:- Tech debt compounds: Tideswell’s architecture made certain improvements expensive. Changing the database, auth system, or queue would have required touching every file.
- Clean slate enables experimentation: We could try RSC, Zustand, Zod, and other modern tools without worrying about breaking existing code.
- Documentation opportunity: A fresh codebase meant we could document everything from day one, rather than trying to document organic growth.
- Type safety throughout: TypeScript in a greenfield project is trivial. TypeScript retrofitted onto a JavaScript codebase is painful.
Going Forward
Terra is now the primary platform. Tideswell remains operational for existing deployments, but new features land in Terra first. Our commitment:- Feature parity: Anything Tideswell can do, Terra can do
- Migration support: We’ll help teams move from Tideswell to Terra
- Shared learnings: Bugs found in Tideswell inform Terra’s design
Summary
| Aspect | Tideswell | Terra |
|---|---|---|
| Database | MongoDB | PostgreSQL |
| Framework | Express + EJS | Next.js + RSC |
| Auth | Custom | WorkOS |
| Queue | File + Memory | Database |
| Language | JavaScript | TypeScript |
| State | Redux | Zustand |
| Forms | Formik | react-hook-form |
| Styling | SCSS | Tailwind |