Skip to main content
Back to portfolioMulti-Tenant SaaS / Commerce

Caddi

Multi-tenant link-in-bio SaaS with a full commerce stack — cart, guest checkout, orders, digital delivery, coupons, and Stripe subscriptions.

Caddi
Domains in architecture
3
Subscription grace period
7 days
Plan enforcement axes
6+
Commerce stack
Full

Why We Built It

Link-in-bio tools have been dominated by products that treat commerce as an afterthought. A checkout button hands the audience off to Gumroad or Shopify. A "shop" page is a link list in disguise. The experience splits: the creator's audience lives on the link page, but actually buying something lives somewhere else, on a surface the creator does not control.

The thesis for Caddi was that commerce should be a first-class primitive of the page itself, not a bolt-on. Cart, guest checkout, orders, digital delivery, coupons, subscription management — all embedded inside the same editor creators already use to build their page. The audience never leaves.

The harder constraint was doing this as a multi-tenant SaaS that one operator could actually run. Every creator needs isolated data, their own subdomain, their own plan enforcement, their own subscription state. And the whole system has to be fast, secure, and recoverable under solo operation.

How It's Being Built

The architecture is a three-domain system. One domain hosts the authoring editor where creators build their pages. A second hosts the public-facing pages their audience visits. A third acts as a CDN origin for user-uploaded media. Each domain has a distinct role in the security and caching model — the public pages can be cache-friendly without compromising the authenticated editor surface, and user media stays behind strict CORS and content-type validation.

The auth posture is deliberately strict for a platform in this category: __Host- cookie prefix for session tokens, dual JWT rotation, security_stamp-based session invalidation. These are the patterns you expect from a dedicated auth provider, not a SaaS side project — but for a multi-tenant commerce platform they are non-negotiable. Session compromise has to be recoverable without logging every tenant out.

The commerce stack is built from scratch against the same multi-tenant data model the editor and public pages read from. Cart, guest checkout, orders, digital delivery, coupons, and Stripe subscriptions all live in one codebase. Plan enforcement runs across six-plus dimensions — max sites, allowed block types, custom domain, visual effects, analytics, commerce features — and every tier gets evaluated at request time rather than at signup.

What It Is When Complete

A three-domain Next.js application stack. The editor is an authenticated admin surface. The public pages are cache-friendly and tenant-isolated at the path level. The static CDN serves user media with strict CORS and content-type validation. The three domains cooperate; they do not share a single surface, because the security and caching requirements are genuinely different per role.

A Stripe subscription lifecycle handler with a 7-day grace period for past-due accounts. Webhooks run through a verified handler that writes to the same database the editor and public pages read from, so plan state is reflected everywhere in real time — and billing state is always authoritative from Stripe, never from the application database.

Plan enforcement at the rendering layer, not at signup. Every block type, every commerce feature, every customization option is gated against the current tenant plan. Downgrading a subscription does not break a creator’s existing page — it hides or disables the premium features until they upgrade again. The page stays recoverable; the features just turn off.

Analytics on an in-memory event buffer that rolls up hourly via a cron job. GDPR-compliant by design — no IP storage, no cookies for non-authenticated visitors — stored in the same PostgreSQL database as the rest of the app, so the creator's dashboard queries do not fan out across systems.

Where It Is

Caddi is a single-operator SaaS with a complete commerce stack, three-domain architecture, and multi-tenant data isolation. Every commerce feature that competitors treat as a pro add-on is in the default build; the tiering is about scale and polish, not about whether commerce works at all.

The authentication posture is stricter than the norm in the link-in-bio category. __Host- cookies, JWT rotation, security_stamp invalidation — the whole setup was written so that session compromise is a recoverable event, not a platform-wide logout.

Subscription lifecycle is fully automated. Upgrades, downgrades, grace period handling, and plan enforcement are all driven by Stripe webhooks without manual intervention. The pattern is: Stripe is the source of truth, the application database is the cache, and the rendering layer reads the cache with an enforcement policy on top.

The whole system is operated solo. Three-domain architecture, multi-tenant database, full commerce stack, subscription lifecycle — all maintained by one person. That was the original design constraint, not an afterthought, and it shapes every architectural choice upstream.

Tech Stack

Next.js 16TypeScriptPostgreSQLDrizzle ORMStripeJWT + __Host- cookiesDockerHetznerCloudflareGitHub Actions

More work like this

The portfolio has more shipped products. About me covers the background and philosophy that connects them.