Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
0bc0b51
feat: add user accounts, domain verification flow, and expiry notific…
jakejarvis Dec 4, 2025
ba9bc0d
feat: implement authentication and settings modals, enhance dashboard…
jakejarvis Dec 4, 2025
f312dcd
feat: enhance TrackedDomainCard and TrackedDomainsTable with Screensh…
jakejarvis Dec 4, 2025
1a7d41c
feat: implement notification preferences for users and domains, inclu…
jakejarvis Dec 4, 2025
8d1852c
Update AGENTS.md and README.md
jakejarvis Dec 4, 2025
5415279
feat: add @react-email/preview-server dependency and update email tem…
jakejarvis Dec 4, 2025
1cc0790
feat: implement notification handling for domain and certificate expi…
jakejarvis Dec 4, 2025
f75a4e9
feat: add functions to clear domain and certificate expiry notificati…
jakejarvis Dec 4, 2025
269234a
chore: add static layout and new pages for Privacy Policy and Terms o…
jakejarvis Dec 4, 2025
25cc83a
fix: update styling for login components and sign-in button for impro…
jakejarvis Dec 4, 2025
1b07cc5
refactor: remove unused authentication and settings intercepting routes
jakejarvis Dec 4, 2025
8c5b61c
fix: adjust CSS class for domain report header to improve layout cons…
jakejarvis Dec 4, 2025
2878d24
fix: add error handling for update and reset notification overrides t…
jakejarvis Dec 4, 2025
bd04c13
refactor: remove unused variable in DomainNotificationRow and enhance…
jakejarvis Dec 4, 2025
3f1e06a
feat: enhance authentication UI with mobile menu and theme toggle fun…
jakejarvis Dec 4, 2025
ed6af4f
feat: add onNavigate callback to LoginContent for improved navigation…
jakejarvis Dec 4, 2025
ced1924
refactor: implement settings management components for notification p…
jakejarvis Dec 4, 2025
9c46cb9
refactor: replace LoginContent with LoginDialog for improved authenti…
jakejarvis Dec 4, 2025
cbbc61b
feat: integrate Polar subscription management for Pro tier, including…
jakejarvis Dec 5, 2025
d119072
feat: implement subscription management features including subscripti…
jakejarvis Dec 5, 2025
e6b7692
feat: add email templates and functionality for Pro subscription life…
jakejarvis Dec 5, 2025
4941275
feat: enhance dashboard components with loading and disabled states f…
jakejarvis Dec 5, 2025
f164da6
feat: implement user subscription management features, including new …
jakejarvis Dec 5, 2025
e1a52f1
feat: enhance DashboardBanner with logging for user interactions and …
jakejarvis Dec 5, 2025
e014157
feat: add subscription expiry notification system, including new data…
jakejarvis Dec 5, 2025
56356b3
feat: implement confirmation dialog for domain removal and archiving …
jakejarvis Dec 5, 2025
d4d0e34
feat: add cleanup functionality for stale unverified domains, impleme…
jakejarvis Dec 5, 2025
179e394
feat: implement add domain dialog with multi-step verification proces…
jakejarvis Dec 5, 2025
19c465a
feat: improve error handling for domain reactivation and enhance arch…
jakejarvis Dec 5, 2025
2264306
feat: integrate nuqs for domain filtering and sorting, enhance dashbo…
jakejarvis Dec 5, 2025
6ac254a
feat: implement bulk actions for domain management, including bulk ar…
jakejarvis Dec 5, 2025
7c680c8
feat: enhance dashboard functionality with active/archived tab naviga…
jakejarvis Dec 5, 2025
02e2e81
feat: add user tier handling to dashboard components, implement upgra…
jakejarvis Dec 5, 2025
b202024
fix: update SelectableDomainCard to enhance selection UI with improve…
jakejarvis Dec 6, 2025
26e88ac
refactor: enhance dashboard components with improved selection handli…
jakejarvis Dec 6, 2025
25ba7b1
refactor: clean dashboard structure by separating content into a dedi…
jakejarvis Dec 6, 2025
effd27b
feat: introduce error handling component for dashboard, enhance loadi…
jakejarvis Dec 6, 2025
874eb7d
feat: add client-side domain validation and error handling to StepEnt…
jakejarvis Dec 6, 2025
e52be90
feat: enhance domain verification UI by introducing CopyableField com…
jakejarvis Dec 6, 2025
d78a9dc
feat: refactor subscription management components to utilize custom h…
jakejarvis Dec 6, 2025
83c3dfd
feat: improve CopyableField component by adding cleanup logic on unmo…
jakejarvis Dec 6, 2025
fa63fcf
feat: implement auto-verification for pending domains with a smart re…
jakejarvis Dec 6, 2025
a946f6c
feat: refactor AddDomainDialog component to utilize custom useDomainV…
jakejarvis Dec 6, 2025
5290e09
feat: add mounted state tracking in useCustomerPortal and useUpgradeC…
jakejarvis Dec 6, 2025
4353207
feat: implement MultiSelect component for enhanced multi-option selec…
jakejarvis Dec 6, 2025
25f013e
fix: update import structure in step-verify-ownership component to ut…
jakejarvis Dec 6, 2025
7a4bc65
feat: add quick redirect for unauthenticated users accessing the dash…
jakejarvis Dec 6, 2025
494e4c4
feat: introduce AnnouncementPill component for user notifications and…
jakejarvis Dec 6, 2025
fbc4954
feat: add AnimatedBackground component to enhance login page with dyn…
jakejarvis Dec 6, 2025
ac4cd8c
refactor: update AddDomainDialog and useDomainVerification hooks to i…
jakejarvis Dec 6, 2025
b1bec14
refactor: simplify verification failure handling in StepVerifyOwnersh…
jakejarvis Dec 6, 2025
2969ef1
refactor: update auto-verification logic for pending domains to use c…
jakejarvis Dec 6, 2025
c7a7a09
refactor: enhance type safety in DomainFilters by introducing FilterC…
jakejarvis Dec 6, 2025
317de2e
refactor: update Polar API environment detection to use VERCEL_ENV an…
jakejarvis Dec 6, 2025
c1f74ce
refactor: enhance AnimatedBackground component to respect prefers-red…
jakejarvis Dec 6, 2025
c891d62
refactor: update auto-verification logic for pending domains to allow…
jakejarvis Dec 6, 2025
2bd1606
refactor: enhance DomainFilters component by adding search filter chi…
jakejarvis Dec 6, 2025
0f99e51
refactor: implement atomic unarchive with limit checking for tracked …
jakejarvis Dec 6, 2025
1c3fbf8
refactor: improve DomainFilters component by removing unnecessary sor…
jakejarvis Dec 6, 2025
97b25b7
refactor: streamline SettingsPage and SettingsContent components by r…
jakejarvis Dec 6, 2025
6790b29
refactor: replace loading skeleton in DashboardLoading with Dashboard…
jakejarvis Dec 6, 2025
538601f
refactor: enhance AGENTS documentation with detailed descriptions of …
jakejarvis Dec 6, 2025
5b85583
refactor: optimize domain management in DashboardContent by using act…
jakejarvis Dec 6, 2025
1945bbe
feat: integrate dynamic tier limits across dashboard components, enha…
jakejarvis Dec 6, 2025
368dac4
feat: add support for configurable Polar product IDs in environment v…
jakejarvis Dec 6, 2025
618be1e
feat: add subscription end date display in dashboard components for i…
jakejarvis Dec 6, 2025
280a34c
fix: implement idempotency keys for Pro upgrade, welcome, and subscri…
jakejarvis Dec 6, 2025
97ec978
refactor: centralize domain mutation logic in useDomainMutations hook…
jakejarvis Dec 6, 2025
789ac0c
fix: replace date formatting in TrackedDomainCard and TrackedDomainsT…
jakejarvis Dec 6, 2025
e766d95
refactor: improve URL parameter handling in DashboardContent and stre…
jakejarvis Dec 6, 2025
e6854ca
refactor: update URL parameter handling in DashboardContent to utiliz…
jakejarvis Dec 6, 2025
111fcf8
chore: bump deps
jakejarvis Dec 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ POSTHOG_ENV_ID=
# Postgres connection string (with credentials)
DATABASE_URL=

# Authentication (better-auth)
BETTER_AUTH_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# Polar (payments/subscriptions)
POLAR_ACCESS_TOKEN=
POLAR_WEBHOOK_SECRET=
# Optional: override product IDs for staging (defaults to production IDs)
POLAR_MONTHLY_PRODUCT_ID=
POLAR_YEARLY_PRODUCT_ID=

# Resend API for email notifications
RESEND_API_KEY=
RESEND_FROM_EMAIL=

# Inngest credentials (set by Inngest integration)
INNGEST_EVENT_KEY=
INNGEST_SIGNING_KEY=
Expand Down
185 changes: 178 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@

## Project Structure & Module Organization
- `app/` Next.js App Router. Default to server components; keep `app/page.tsx` and `app/api/*` thin and delegate to `server/` or `lib/`.
- `app/dashboard/` Protected dashboard for domain tracking with Active/Archived tabs.
- `components/` reusable UI primitives (kebab-case files, PascalCase exports).
- `hooks/` shared stateful helpers (camelCase named exports).
- `components/auth/` Authentication components (sign-in button, user menu, login content).
- `components/dashboard/` Dashboard components (domain cards, tables, settings, add domain dialog, upgrade prompt, subscription section, archived domains view, bulk actions toolbar, domain filters, health summary).
- `emails/` React Email templates for notifications (domain expiry, certificate expiry, verification status, subscription lifecycle).
- `hooks/` shared stateful helpers (camelCase named exports): `useDomainFilters`, `useSelection`, `useSortPreference`, `useViewPreference`, `useDomainExport`, `useCustomerPortal`, `useUpgradeCheckout`, `useLogger`, etc.
- `lib/` domain utilities and shared modules; import via `@/...` aliases.
- `lib/constants/` modular constants organized by domain (app, decay, domain-validation, external-apis, headers, ttl).
- `lib/inngest/` Inngest client and functions for event-driven background section revalidation.
- `lib/auth.ts` better-auth server configuration with Drizzle adapter.
- `lib/auth-client.ts` better-auth client for React hooks (`useSession`, `signIn`, `signOut`).
- `lib/constants/` modular constants organized by domain (app, decay, domain-filters, domain-validation, notifications, pricing-providers, sections, tier-limits, headers, ttl).
- `lib/inngest/` Inngest client and functions for background jobs (section revalidation, expiry checks, domain re-verification).
- `lib/db/` Drizzle ORM schema, migrations, and repository layer for Postgres persistence.
- `lib/db/repos/` repository layer for each table (domains, certificates, dns, favicons, headers, hosting, providers, registrations, screenshots, seo).
- `lib/db/repos/` repository layer for each table (domains, certificates, dns, favicons, headers, hosting, notifications, providers, registrations, screenshots, seo, tracked-domains, user-notification-preferences, user-subscription, users).
- `lib/logger/` unified structured logging system with OpenTelemetry integration, correlation IDs, and PII-safe field filtering.
- `lib/polar/` Polar subscription integration (products config, webhook handlers, downgrade logic, subscription emails).
- `lib/resend.ts` Resend email client for sending notifications.
- `lib/schemas/` Zod schemas organized by domain.
- `server/` backend integrations and tRPC routers; isolate DNS, RDAP/WHOIS, TLS, and header probing services.
- `server/routers/` tRPC router definitions (`_app.ts` and domain-specific routers).
- `server/services/` service layer for domain data fetching (DNS, certificates, headers, hosting, registration, SEO, screenshot, favicon, etc.).
- `server/routers/` tRPC router definitions (`_app.ts`, `domain.ts`, `tracking.ts`).
- `server/services/` service layer for domain data fetching (DNS, certificates, headers, hosting, registration, SEO, screenshot, favicon, verification).
- `public/` static assets; Tailwind v4 tokens live in `app/globals.css`. Update `instrumentation-client.ts` when adding analytics.
- `trpc/` tRPC client setup, query client, and error handling.
- `trpc/` tRPC client setup, query client, error handling, and `protectedProcedure` for auth-required endpoints.

## Build, Test, and Development Commands
- `pnpm dev` — start all local services (Postgres, Inngest, etc.) and Next.js dev server at http://localhost:3000 using `concurrently`.
Expand Down Expand Up @@ -74,6 +83,7 @@
- Keep secrets in `.env.local`. See `.env.example` for required variables.
- Vercel Edge Config provides dynamic, low-latency configuration without redeployment:
- `domain_suggestions` (array): Homepage domain suggestions; fails gracefully to empty array
- `tier_limits` (object): `{ free: 5, pro: 50 }` for domain tracking limits per tier
- Vercel Blob backs favicon/screenshot storage with automatic public URLs; metadata cached in Postgres.
- Screenshots (Puppeteer): prefer `puppeteer-core` + `@sparticuz/chromium` on Vercel.
- Persist domain data in Postgres via Drizzle with per-table TTL columns (`expiresAt`).
Expand All @@ -83,6 +93,167 @@
- Use Next.js 16 `after()` for fire-and-forget background operations (analytics, domain access tracking) with graceful degradation.
- Review `trpc/init.ts` when extending procedures to ensure auth/context remain intact.

## Authentication (better-auth)
- **Server config:** `lib/auth.ts` - betterAuth with Drizzle adapter, GitHub OAuth, Polar plugin for subscriptions.
- **Client hooks:** `lib/auth-client.ts` - `useSession`, `signIn`, `signOut`, `getSession`, `checkout`, `customerPortal`.
- **Protected routes:** Dashboard layout (`app/dashboard/layout.tsx`) checks session server-side and redirects to `/login`.
- **tRPC integration:** `trpc/init.ts` exports `protectedProcedure` that requires valid session; throws `UNAUTHORIZED` otherwise.
- **Schema tables:** `users`, `sessions`, `accounts`, `verifications` in `lib/db/schema.ts`.
- **Login modal:** Uses simple React dialog triggered from header; fallback full page at `/login`.
- **Environment variables:** `BETTER_AUTH_SECRET`, `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`.

## Domain Tracking System
The domain tracking feature allows authenticated users to track domains they own, receive expiration notifications, and manage notification preferences.

### Core Tables
- `tracked_domains`: Links users to domains with verification status, token, per-domain notification overrides, and `archivedAt` for soft-archiving.
- `user_subscriptions`: User tier (free/pro), `endsAt` for canceled-but-active subscriptions, and `lastExpiryNotification` for tracking sent reminders.
- `user_notification_preferences`: Global notification toggles (domainExpiry, certificateExpiry, verificationStatus).
- `notifications`: History of sent notifications with Resend email ID for troubleshooting.

### Subscription Repository (`lib/db/repos/user-subscription.ts`)
- `getUserSubscription`: Get user's tier, max domains, and subscription end date.
- `updateUserTier`: Upgrade/downgrade user tier (creates missing record if needed).
- `setSubscriptionEndsAt` / `clearSubscriptionEndsAt`: Track canceled subscription end dates.
- `getUsersWithEndingSubscriptions`: Query for subscription expiry cron job.
- `setLastExpiryNotification`: Track which expiry notifications have been sent.

### Domain Verification
Users must verify domain ownership via one of three methods:
1. **DNS TXT record:** Add `_domainstack-verify.domain.com TXT "token"`.
2. **HTML file:** Upload `/.well-known/domainstack-verify.txt` containing the token.
3. **Meta tag:** Add `<meta name="domainstack-verify" content="token">` to homepage.

Verification service: `server/services/verification.ts` with `tryAllVerificationMethods()` and `verifyDomainOwnership()`.

### Re-verification & Grace Period
- Inngest function `reverifyDomains` runs daily at 4 AM UTC.
- Auto-verifies pending domains (users who added verification but never clicked "Verify").
- Re-verifies existing domains; if failing, enters 7-day grace period before revocation.
- Sends `verification_failing` email on first failure, `verification_revoked` on revocation.

### Notification System
- **Categories:** `domainExpiry`, `certificateExpiry`, `verificationStatus` (defined in `lib/constants/notifications.ts`).
- **Thresholds:** Domain expiry: 30, 14, 7, 1 days. Certificate expiry: 14, 7, 3, 1 days.
- **Per-domain overrides:** `notificationOverrides` JSONB column; `undefined` = inherit from global, explicit `true/false` = override.
- **Idempotency:** Notification records created before email send; Resend idempotency keys prevent duplicates on retry.
- **Troubleshooting:** `resendId` column stores Resend email ID for delivery debugging.

### tRPC Router (`server/routers/tracking.ts`)
Key procedures:
- `addDomain`: Add domain to tracking (or resume unverified). Triggers `auto-verify-pending-domain` Inngest job.
- `verifyDomain`: Verify ownership.
- `removeDomain`: Delete tracked domain.
- `archiveDomain` / `unarchiveDomain`: Soft-archive or reactivate domains.
- `bulkArchiveDomains` / `bulkRemoveDomains`: Bulk operations on multiple domains (parallel execution, max 100).
- `listDomains` / `listArchivedDomains`: Get active or archived tracked domains.
- `getLimits`: Get user's tier, active/archived counts, max domains, and `subscriptionEndsAt` for canceled-but-active subscriptions.
- `getNotificationPreferences` / `updateGlobalNotificationPreferences`: Global toggles.
- `updateDomainNotificationOverrides` / `resetDomainNotificationOverrides`: Per-domain overrides.

### Inngest Background Jobs
- `check-domain-expiry`: Daily at 9 AM UTC; sends domain expiration notifications.
- `check-certificate-expiry`: Daily at 10 AM UTC; sends certificate expiration notifications.
- `check-subscription-expiry`: Daily at 9:30 AM UTC; sends Pro subscription expiry reminders at 7, 3, and 1 days before end.
- `reverify-domains`: Daily at 4 AM UTC; auto-verifies pending and re-verifies existing domains.
- `cleanup-stale-domains`: Weekly on Sundays at 3 AM UTC; deletes unverified domains older than 30 days.
- `auto-verify-pending-domain`: Event-driven; auto-verifies newly added domains with smart retry schedule (1m, 3m, 10m, 30m, 1hr).

## Email Notifications (Resend + React Email)
- **Client:** `lib/resend.ts` exports `resend` client and `RESEND_FROM_EMAIL`.
- **Templates:** `emails/` directory with React Email components:
- `domain-expiry.tsx` - Domain expiration reminders (30, 14, 7, 1 days before)
- `certificate-expiry.tsx` - SSL certificate expiration alerts (14, 7, 3, 1 days before)
- `verification-failing.tsx` - Domain verification started failing (7-day grace period begins)
- `verification-revoked.tsx` - Domain verification revoked (grace period expired)
- `pro-upgrade-success.tsx` - Welcome email when Pro subscription becomes active
- `pro-welcome.tsx` - Tips email sent after Pro upgrade
- `subscription-canceling.tsx` - Confirmation when subscription is canceled (still active until period end)
- `subscription-expired.tsx` - Notification when Pro access ends with archived domain count
- **Subscription emails:** `lib/polar/emails.ts` exports `sendProUpgradeEmail()`, `sendSubscriptionCancelingEmail()`, `sendSubscriptionExpiredEmail()`.
- **Idempotency:** Use `generateIdempotencyKey(trackedDomainId, notificationType)` and pass to `resend.emails.send()`.
- **Pattern:** Create notification record → Send email → Update with `resendId` for troubleshooting.
- **Environment variables:** `RESEND_API_KEY`, `RESEND_FROM_EMAIL`.

## Subscriptions (Polar)
Polar handles Pro tier subscriptions with automatic tier management via webhooks.

### Product Configuration
- **Config file:** `lib/polar/products.ts` defines products with IDs, slugs, tiers, and pricing.
- **Pro tier:** Two products for billing flexibility: `pro-monthly` ($2/month) and `pro-yearly` ($20/year).
- **Checkout:** Pass both product IDs to let users choose billing interval at checkout.

### Integration
- **Server:** `lib/auth.ts` includes Polar plugin with `checkout`, `portal`, and `webhooks` handlers.
- **Client:** `lib/auth-client.ts` exports `checkout()` and `customerPortal()` for UI triggers.
- **Webhook handlers:** `lib/polar/handlers.ts`:
- `handleSubscriptionCreated` - logs subscription initiation (payment may still be pending)
- `handleSubscriptionActive` - upgrades tier after payment confirmed, clears any pending cancellation
- `handleSubscriptionCanceled` - stores `subscriptionEndsAt` to show banner (user keeps access until period ends)
- `handleSubscriptionRevoked` - triggers downgrade and clears `subscriptionEndsAt`
- **Downgrade logic:** `lib/polar/downgrade.ts` archives oldest domains beyond free tier limit.

### UI Components
- **Upgrade prompt:** `components/dashboard/upgrade-prompt.tsx` - contextual banner when near/at domain limit.
- **Subscription ending banner:** `components/dashboard/subscription-ending-banner.tsx` - shows when subscription is canceled but still active.
- **Dashboard banner:** `components/dashboard/dashboard-banner.tsx` - generic banner with variants (info, warning, success, danger, pro).
- **Subscription section:** `components/dashboard/subscription-section.tsx` - settings page with plan info and manage/upgrade buttons.
- **Archived domains:** `components/dashboard/archived-domains-view.tsx` - view and reactivate archived domains.

### Domain Archiving
- Archived domains don't count against user's limit.
- Users can manually archive/unarchive from dashboard tabs.
- On downgrade, oldest domains are auto-archived to enforce free tier limit.
- Unarchiving checks capacity before allowing reactivation.

### Dashboard Features
- **Filtering:** URL-persisted filters via `nuqs` (search, status, health, TLDs). Hook: `hooks/use-domain-filters.ts`.
- **Bulk actions:** Multi-select with floating toolbar for archive/delete. Hook: `hooks/use-selection.ts`. Component: `components/dashboard/bulk-actions-toolbar.tsx`.
- **Health summary:** Clickable badges showing expiring/pending counts. Component: `components/dashboard/health-summary.tsx`.
- **View modes:** Grid (sortable) and table (column sorting via TanStack Table). Hooks: `hooks/use-view-preference.ts`, `hooks/use-sort-preference.ts`.
- **Filter constants:** `lib/constants/domain-filters.ts` defines `STATUS_OPTIONS` and `HEALTH_OPTIONS`.

### Environment variables
- `POLAR_ACCESS_TOKEN`: API token from Polar dashboard.
- `POLAR_WEBHOOK_SECRET`: Webhook secret for signature verification.

## TanStack Query Best Practices
Dashboard components use optimistic updates for responsive UX:

```typescript
const removeMutation = useMutation({
...trpc.tracking.removeDomain.mutationOptions(),
onMutate: async ({ trackedDomainId }) => {
await queryClient.cancelQueries({ queryKey: domainsQueryKey });
const previousDomains = queryClient.getQueryData(domainsQueryKey);

// Optimistically update
queryClient.setQueryData(domainsQueryKey, (old) =>
old?.filter((d) => d.id !== trackedDomainId)
);

return { previousDomains }; // Snapshot for rollback
},
onError: (err, _variables, context) => {
// Rollback on error
if (context?.previousDomains) {
queryClient.setQueryData(domainsQueryKey, context.previousDomains);
}
},
onSettled: () => {
// Always invalidate to ensure consistency
void queryClient.invalidateQueries({ queryKey: domainsQueryKey });
},
});
```

Key patterns:
- Use `onMutate` for optimistic updates with snapshot.
- Use `onError` for rollback.
- Use `onSettled` (not `onSuccess`) for invalidation—runs on both success and error.
- Call `cancelQueries` before optimistic update to prevent race conditions.
- Use `typeof query.data` for type-safe updaters.

## Analytics & Observability
- Uses **PostHog** for analytics and error tracking with reverse proxy via `/_proxy/ingest/*`.
- PostHog sourcemap uploads configured in `next.config.ts` with `@posthog/nextjs-config`.
Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@
- **Instant domain reports**: Registration, DNS, certificates, HTTP headers, hosting & email, and geolocation.
- **SEO insights**: Extract titles, meta tags, social previews, canonical data, and `robots.txt` signals.
- **Screenshots & favicons**: Server-side screenshots and favicon extraction, cached in Postgres with Vercel Blob storage.
- **Fast, private, no sign-up**: Live fetches with intelligent multi-layer caching.
- **Fast, private, no sign-up required for reports**: Live fetches with intelligent multi-layer caching.
- **Domain tracking dashboard**: Sign in with GitHub to track domains you own, verify ownership, and receive expiration alerts.
- **Pro tier subscriptions**: Upgrade via [Polar](https://polar.sh) for expanded domain tracking limits (50 vs 5 domains).
- **Email notifications**: Configurable alerts for domain expiration, SSL certificate expiration, subscription lifecycle, and verification status changes.
- **Reliable data pipeline**: Postgres persistence with per-table TTLs (Drizzle) and event-driven background revalidation (Inngest).
- **Dynamic configuration**: Vercel Edge Config for runtime-adjustable domain suggestions without redeployment.
- **Advanced dashboard**: Domain filtering by status/health/TLD, URL-persisted filters, bulk archive/delete actions, and sortable table/grid views.
- **Dynamic configuration**: Vercel Edge Config for runtime-adjustable domain suggestions and tier limits without redeployment.

## 🛠️ Tech Stack

- **Next.js 16** (App Router) + **React 19** + **TypeScript**
- **Tailwind CSS v4**
- **tRPC** API
- **PlanetScale Postgres** + **Drizzle ORM** with connection pooling
- **Inngest** for event-driven background revalidation with built-in concurrency control
- **Vercel Edge Config** for runtime configuration (domain suggestions)
- **Tailwind CSS v4** + **shadcn/ui** components
- **tRPC** API with **TanStack Query** for data fetching and optimistic updates
- **TanStack Table** for sortable dashboard table view
- **Vercel Postgres** + **Drizzle ORM** with connection pooling
- **Better Auth** for authentication with GitHub OAuth
- **Polar** for subscription payments and customer portal (Pro tier)
- **Inngest** for event-driven background jobs (revalidation, expiry checks, domain re-verification)
- **Resend** + **React Email** for transactional email notifications
- **Vercel Edge Config** for runtime configuration (domain suggestions, tier limits)
- **Vercel Blob** for favicon/screenshot storage with Postgres metadata caching
- [**rdapper**](https://github.com/jakejarvis/rdapper) for RDAP lookups with WHOIS fallback
- **Puppeteer** (with `@sparticuz/chromium` on Vercel) for server-side screenshots
Expand Down
Loading