Skip to main content

Technology Stack

Terra is built on a modern, serverless-first architecture. Each choice reflects a hypothesis we’re testing about building government intake infrastructure.

Monorepo Structure

Terra uses a Turborepo monorepo with pnpm workspaces:
unify-platform/
├── apps/
│   ├── terra/              # Main Next.js application
│   └── docs/               # Mintlify documentation
├── packages/
│   └── ui/                 # @unify/ui - Shared component library
├── turbo.json              # Turborepo configuration
└── pnpm-workspace.yaml     # Workspace definitions

Why Monorepo?

  1. Shared Components: The @unify/ui package contains all Shadcn UI components, enabling reuse across future apps
  2. Atomic Deploys: Turborepo ensures dependent packages build in the correct order
  3. Single Lock File: pnpm workspaces share dependencies, reducing install times and disk usage

@unify/ui Package

All UI primitives (Button, Card, Input, Dialog, etc.) live in packages/ui:
// Import from the shared package
import { Button, Card, Input } from "@unify/ui";
This keeps apps/terra focused on business logic while UI components remain reusable.

Frontend

Next.js 16

App Router with React 19 Server Components. Server-side rendering keeps sensitive logic off the client.

Tailwind CSS 4

Utility-first CSS with Shadcn UI components built on Radix primitives.

Core Libraries

LibraryVersionPurpose
react-hook-form7.66Form state management with minimal re-renders
zod4.1Runtime schema validation for forms and API inputs
zustand5.0Lightweight state management for the form builder
@dnd-kit6.3Drag-and-drop for form builder and folder organization
framer-motion12.xAnimations and micro-interactions
recharts2.15Dashboard charts and visualizations
lucide-react0.555Icon library

Why Next.js App Router?

We’re testing whether React Server Components can improve security posture for government applications:
  • Server-side by default: Sensitive operations (database queries, auth checks) never reach the client bundle
  • Streaming: Large forms render progressively, improving perceived performance
  • Edge-ready: Middleware runs at the edge for fast auth checks globally

Backend

Supabase

PostgreSQL with Row Level Security, Auth helpers, and private Storage buckets.

WorkOS AuthKit

Enterprise-ready SSO (SAML, OIDC) with 1M free monthly active users.

Why Supabase?

We’re testing whether pushing authorization into the database layer reduces application risk:
-- Example: Row Level Security policy
CREATE POLICY "Users can only view their own submissions"
ON submissions FOR SELECT
USING (user_id = auth.uid());
Hypothesis: If the database enforces access control, application bugs are less likely to expose data. Storage: All file uploads go to private buckets. Zero public URLs—files are only accessible via 60-second signed URLs generated server-side.

Why WorkOS?

We chose WorkOS over Supabase Auth for several reasons:
  1. Enterprise SSO Ready: Government agencies often require SAML/OIDC integration with Azure AD, Okta, or Google Workspace. WorkOS makes this trivial.
  2. Generous Free Tier: 1 million monthly active users on the free plan—enough for most programs.
  3. Clean Developer Experience: AuthKit handles session management, token refresh, and middleware with minimal boilerplate.
  4. Future-Proof: Directory Sync and Audit Logs align with government compliance requirements.

Email

Resend

Transactional email with React Email templates. Production domain: notifications.withunify.org

Why Resend?

Resend handles lightweight, brandable receipts—the “your application was received” emails that applicants expect immediately. Complex notification workflows (reminders, status updates, multi-channel delivery) remain in external systems. This keeps Terra focused on intake, not becoming a notification platform.
// React Email template example
<SubmissionReceipt
  applicantName="Jane Doe"
  programName="Rental Assistance"
  submissionId="APP-2024-001"
  portalUrl="https://portal.example.gov"
/>

Translations

DeepL

Machine translation API for auto-translating form content and UI strings.

Why DeepL?

Government forms must serve diverse populations. DeepL offers:
  1. Superior Quality: Consistently better translations than Google Translate for formal documents
  2. Privacy-Conscious: Data not used for training models (important for government)
  3. Simple API: Single endpoint for translation, no complex setup
  4. Generous Free Tier: 500,000 characters/month on the free API
DeepL supports 14 languages with auto-translate. Languages not supported by DeepL (Somali, Vietnamese, etc.) can still be added for manual translation. See the Translations Guide for implementation details.

Address Verification

Smarty Streets

USPS address autocomplete and validation for accurate mail delivery.

Why Smarty Streets?

Government programs require accurate addresses for benefit delivery. Smarty Streets offers:
  1. USPS Certified: Official CASS-certified address validation
  2. Autocomplete: Real-time address suggestions as users type (requires US Autocomplete Pro subscription)
  3. Standardization: Converts “123 main st” to “123 Main Street NW”
  4. Deliverability: DPV codes indicate if mail can actually be delivered

How It Works

When Smarty credentials are configured:
  1. User starts typing → Autocomplete suggestions appear after 3 characters
  2. User selects suggestion → All address fields auto-fill + ”✓ Verified” badge appears
  3. User manually edits → Badge changes to ”⚠ Unverified”
This provides frictionless UX while clearly indicating whether the address has been validated.

Smarty Products Required

ProductPurpose
US Autocomplete ProType-ahead suggestions ($21/month)
US Street APIAddress verification (free tier: 250/month)
Address fields work without Smarty—users just type manually without autocomplete.

Analytics

PostHog

Product analytics for understanding form completion rates and drop-off points.

Security

DOMPurify

XSS sanitization for user-generated HTML content (isomorphic-dompurify for SSR).

Aikido

Automated SAST scanning and dependency vulnerability detection.

Security Utilities

Terra includes centralized security functions in src/lib/security.ts:
FunctionPurpose
getSafeRedirectPath()Prevents open redirect attacks
sanitizeStoragePath()Prevents path traversal in file storage
cleanFileName()Sanitizes user-provided filenames
DOMPurify.sanitize()Strips malicious scripts from HTML
See the Security Guide for implementation details.

Developer Tools

# Build System
turborepo           # Monorepo task orchestration
pnpm                # Fast, disk-efficient package manager

# Testing
vitest              # Unit tests (React Testing Library)
playwright          # E2E tests (multi-browser)

# Code Quality
typescript 5.x      # Type safety
eslint 9.x          # Linting
tailwindcss 4.x     # CSS

# Security
aikido              # SAST scanning on PRs

# CI/CD
github-actions      # Type check + test on every PR

Test Commands

pnpm test           # Unit tests (Vitest)
pnpm test:e2e       # E2E tests (Playwright)
pnpm lint           # ESLint
pnpm build          # Type check + production build (via Turborepo)
pnpm dev            # Start dev server (auto-injects Doppler secrets)

Database Schema

Terra uses a small number of tables with JSONB for flexibility:
-- Core Tables
forms              -- Form definitions (JSONB schema)
submissions        -- User submissions (JSONB answers)
users              -- User profiles synced from WorkOS
program_members    -- Form-level access control (RBAC)
folders            -- Form organization (drag-and-drop)

-- System Tables
organization_settings  -- Global branding and localization dictionary

Why JSONB for Forms?

See ADR-001: Recursive Form Schema for the full decision record. TL;DR: Government forms have deeply nested conditional logic. JSONB trees are more flexible than normalized tables for this use case.

Secrets Management

Doppler

Centralized secrets management with automatic environment syncing.

Why Doppler?

Managing environment variables across local development, staging, and production is error-prone. Doppler provides:
  1. Single Source of Truth: All secrets in one dashboard
  2. Environment Branching: Separate configs for dev/staging/prod
  3. Team Sync: No more sharing .env files over Slack
  4. Audit Logs: Track who accessed which secrets when
  5. Automatic Rotation: Update secrets without redeploying

Usage

# Install CLI
brew install dopplerhq/cli/doppler

# Login and setup
doppler login
doppler setup

# Run with injected secrets
doppler run -- npm run dev

# Or create an alias
alias terra="doppler run -- npm run dev"

Vercel Integration

Doppler syncs directly to Vercel:
  1. Connect Doppler to Vercel in the Doppler dashboard
  2. Map your Doppler project to your Vercel project
  3. Secrets automatically sync on change
Terra works fine with traditional .env.local files—Doppler is recommended but not required.

Infrastructure

ServiceProviderPurpose
HostingVercelEdge-optimized Next.js deployment
DatabaseSupabaseManaged PostgreSQL + Storage
AuthWorkOSEnterprise SSO and user management
SecretsDopplerCentralized secrets management
EmailResendTransactional receipts
TranslationDeepLAuto-translate form content
AddressSmarty StreetsUSPS address validation
DNSCloudflareDDoS protection and caching

Next: Architecture Decisions

Read the ADRs explaining why we made these choices.