Skip to content
All posts
Strategy

Inside Semoria's Architecture: The Lean Modern SaaS Stack

David PackmanFounder, Agenticise14 min read
Inside Semoria's architecture: the lean modern SaaS stack

A multi-tenant SaaS with full authentication, subscription payments, AI integrations, automation, security hardening, transactional email, analytics, and a free trial, all running for under £40 a month at 50 paying users. That is what a lean modern SaaS stack looks like in 2026 if every layer is chosen as a utility rather than a fashion statement.

Semoria runs on exactly that stack. This post is a walkthrough of every layer, why it was chosen, what it costs at different scales, and how the multi-tenant patterns hold up. Nothing exotic, nothing aspirational. Every figure is from the production codebase.

Want a clear, phased automation roadmap for your business? Book a free 30-minute discovery call.
Book a call

What is on Semoria's lean modern SaaS stack?

Eight layers, each chosen because it is production-grade, well-documented, and easy to integrate with the others. The full list with the one-sentence role each plays:

LayerChoiceRole
App framework and hostingNext.js on VercelServer-rendered React app, automatic deploy on push to main, edge runtime where useful.
Database and authSupabase PostgresPostgres with row-level security on every table, plus auth (email and OAuth) and storage.
Workflow automationn8nContent generation pipelines, scheduled jobs, and the asynchronous side of the publishing flow.
Subscription billingStripeThree live tiers (£29, £59, £99), annual pricing, promo codes, webhook-driven subscription state.
Transactional emailResend27 templates covering trial onboarding, billing events, weekly digest, and feature discovery.
AI inferenceClaude via OpenRouter, Perplexity Sonar Pro, Anthropic HaikuDrafting, research grounding, and lightweight free tools, with a managed tier on paid plans.
Analytics and funnelsPostHogPageviews, conversion funnels, feature usage, A/B testing flags.
Error monitoringSentryFrontend and backend error tracking, release tagging, performance traces.

The choices are deliberately mainstream. The Next.js documentation, Supabase documentation, Stripe billing documentation, and PostHog documentation are all extensive, well-maintained, and pattern-rich, which makes AI-assisted development substantially faster. AI coding agents are better at writing code against well-documented tools because their training data contains many thousands of working examples.

How does multi-tenancy work on Semoria?

Every tenant is one row in an organisations table, every user belongs to one organisation, and every piece of data is tied to an org_id. Row-level security on every table enforces that a user can read or write only their own organisation's rows. That is the entire model.

The detail that matters is that the database itself enforces the tenancy boundary. If a single API route gets a bug and runs a query without an explicit org filter, the query still returns only the calling user's rows, because the row-level security policy adds that filter automatically. The application code is a second line of defence, not the only line. Supabase publishes a first-class guide to row-level security that maps these patterns one-to-one onto Postgres.

The honest list of tables on Semoria's schema looks like this. The organisations table holds tenant identity, plan tier, Stripe identifiers, trial dates, and usage counters. The brand_config table holds voice profile and brand guidelines, one-to-one with organisations. The topics table holds seed keywords and research data per tenant. The content table is the core artefact: a generated post with a lifecycle from draft to pending review to approved or rejected. The content_schedule, activity_log, execution_costs, achievements, notifications, notification_read_status, and notification_preferences tables fill out the rest. Each one has row-level security tied to the calling user's organisation membership.

A few specific patterns hold the system together. First, the billing fields on the organisations table have column-level security on top of the row-level policy, so a user can read its own plan tier without leaking the Stripe customer or subscription identifiers. Second, the api_keys table (which stores encrypted OpenRouter keys for the bring-your-own-key path) has no client-side select policy at all: keys are read server-side only via the service role. Third, the waitlist table dropped its public insert policy after a permissions audit, because waitlist signups now flow through a server route rather than direct client insert. Each of those decisions is a five-minute migration, and each one is the kind of thing that would be expensive to retrofit later.

What runs the AI layer?

Three model providers, each chosen for a different job. Claude (via OpenRouter) handles the drafting because it is the strongest model for tone-of-voice work and the long-form writing tasks that Semoria leans on. Perplexity Sonar Pro handles research grounding, because it returns citations alongside its summaries and the citations are the point. Anthropic Haiku handles the lightweight free tools, because they need to return in seconds for tens of free users per day and the cost-per-call needs to round to nothing.

The pricing model on the AI layer is split across two paths. The Starter tier supports bring-your-own-key, where the user supplies their own OpenRouter key and pays AI inference at cost. Amplify and Pro tiers use a managed key, where AI inference is bundled into the subscription price. That split keeps the unit economics clean: the Starter tier never loses money on inference, and the higher tiers absorb the inference cost into a predictable subscription margin.

Security on the AI layer is a real concern, and it took specific work. Every prompt that hits a model is sanitised against prompt-injection patterns (system-prompt leak attempts, JSON-escape attacks, role-override attempts). Every AI-generated string that comes back is treated as untrusted input before it is rendered. Rate limiting is enforced both per-tenant (so one user cannot exhaust a managed-tier pool) and globally (so a runaway loop cannot exhaust the wallet). The OWASP Top 10 for LLM applications is required reading for anyone building this kind of layer in production, and a chunk of week four on the Semoria build was spent walking the entire codebase against that checklist.

How is the stack hardened for production?

Production hardening is the unglamorous part of the build, and it is also the part that decides whether the platform survives its first real-world traffic. The honest list of what hardening looked like on Semoria:

Row-level security everywhere. Every table has row-level security enabled and policies tied to organisation membership. No exceptions, including read-only audit tables. The Postgres migration that adds the policy is part of the same migration that creates the table.

Column-level security on billing fields. The Stripe identifiers, subscription status, and trial dates have stricter policies than the rest of the organisation row. The client can read what it needs (plan tier, trial expiry) and nothing else.

Server-only secrets. OpenRouter keys, Stripe webhook secrets, Resend API keys, and PostHog server-side keys are never exposed to the browser. The split between client-side (anon) and server-side (service-role) Supabase clients is enforced everywhere by typed import boundaries.

HMAC signature verification on every webhook. Stripe webhooks, n8n webhooks, and any other inbound signed payload is verified against its shared secret before any side effect runs. A webhook with a bad signature returns 400 and is logged but never trusted.

Content Security Policy in report-only mode. A CSP header is set across the site, in report-only mode at launch so genuine breaks are visible without bricking the site, and the policy tightens as the report shows what is actually loading.

Rate limiting on every public endpoint. Per-IP and per-user limits on every authenticated route, plus stricter limits on the free tools and the public waitlist endpoint. Rate limits return a clear 429 with retry-after.

Prompt-injection blocking on every AI call. A short sanitisation step strips and rejects the most common injection patterns before the prompt reaches the model. The remaining edge cases are caught by the system prompt itself, which is written to be robust to in-prompt manipulation.

Middleware that fails closed. The Next.js middleware that protects /dashboard and /onboarding fails to "unauthenticated" on any error, never to "authenticated". The cost of an over-cautious middleware is a re-login; the cost of an under-cautious one is a data leak.

Search-path hardening on Postgres triggers. The update_updated_at() trigger that fires on every table mutation has an explicit search_path so it cannot be hijacked by a malicious extension. This is a small thing that catches a real attack class.

Auth callback redirect validation. The OAuth callback validates the next redirect parameter against an allowlist of safe paths. The open-redirect attack class is gone because of two lines of code.

None of that hardening is exotic. All of it is in the Supabase customer case studies at one production scale or another. The point is that it has to be done deliberately on a six-week timeline, which is why week four of the Semoria build was a polish-and-harden week.

What does the stack cost at different user counts?

Honest unit economics, on the assumption that paid users sit on the Amplify tier (£59 a month) on average and that AI inference is bundled into the subscription:

UsersHosting (Vercel)Database (Supabase)Email (Resend)Analytics + errorsAI inferenceTotal / month
0 users (idle)£0 (free)£0 (free)£0 (free)£0 (free tiers)£0£0
50 paying£16 (Pro)£20 (Pro)£0 (free)£0 (free tiers)around £5around £40
200 paying£16£20£0 (free)£0 to £20 (paid PostHog)around £25around £80
500 paying£40 (extra Vercel team or bandwidth)£40 (extra compute)£15 (Resend Pro)£30 to £60around £65around £200

The figures exclude Stripe transaction fees (about 1.5 percent of revenue plus 20p per transaction in the UK, documented in the Stripe billing pricing). They also exclude the founder's time, which on a bootstrapped SaaS is the largest cost line by a long way and the one that does not show up on any invoice.

The shape that matters is that the stack scales close to linearly with paid users, not as a step function. There is no sudden cliff at 1,000 users where you have to rewrite to a different architecture. The next real architectural decision lands at around 10,000 active users, where AI inference, database read load, and email send volume all start to need careful scaling work. Semoria is engineered to make that transition deferrable for the same reasons it was engineered to fit a six-week build window.

Why this stack rather than no-code or a bespoke build?

The category-level comparison that informed the build choice:

ApproachBuild timelineMonthly run costCeiling on complexitySwitching cost later
Bespoke build on a heavy stack4 to 9 months£500 to £5,000 (managed cloud, observability, infrastructure)No real ceilingLow (everything is already custom code)
Lean modern stack with AI-assisted dev4 to 8 weeks£40 at 50 users, around £200 at 500 usersHolds well past 10,000 users for most SaaS shapesLow (Postgres is portable, code is yours)
No-code platformsDays to weeksLow at first, sharply rising at scaleHard ceiling on customisation and complexityHigh (data and workflows are locked into the platform)

The middle row is where Semoria sits and where most independent SaaS builders in the UK should sit by default. The lean modern stack is the same shape that funded teams use; the difference is that the build itself is faster because AI-assisted development eliminates a huge slice of the boilerplate. The platform is yours, the data is yours, and the cost ceiling stays predictable through the early years when it matters most.

The bespoke stack still wins when the workload is genuinely complex (a high-throughput data pipeline, a heavily regulated workflow, a hardware-adjacent product). The no-code platform still wins when the product is so simple that the customisation ceiling does not matter. Most SaaS ideas, including most B2B platforms, are in the middle row.

What does this prove about how Agenticise builds?

The stack on Semoria is the same stack Agenticise now uses for any platform-grade client engagement, with the same multi-tenant patterns, the same hardening work, and the same cost ceiling at small user counts. The /case-studies/semoria hub is the long-form home for the build. The methodology that made the timeline possible, multi-agent orchestration in Claude Code, is covered in the previous post in this series. The marketing engine that ships alongside the platform, including the programmatic blog, the free tools, the persona pages, and the social automation, is covered in the next post. The original origin story explains why Semoria exists at all.

If you have been quoted six figures or twelve months for a platform that fits the middle row of the table above, it is worth a conversation. The same stack at the same cost ceiling is achievable on a six-to-eight-week build cycle with a small team. That is no longer a future-state pitch. It is what Semoria has been running in production on since the build finished.

Frequently asked questions

What is the cheapest production-ready SaaS stack in 2026?

A lean modern stack of Next.js on Vercel, Supabase Postgres with row-level security, Stripe for billing, Resend for email, n8n for workflow automation, PostHog for analytics, and Sentry for errors will run a production multi-tenant SaaS for around £40 a month at 50 paying users. Every layer has a free tier or low entry price, and each is well-documented, which keeps both engineering time and ongoing tooling cost low. The cost grows roughly linearly with active users, not as a step function, which makes early unit economics easy to model.

Is Supabase production-ready for a UK SaaS?

Yes. Supabase is Postgres with auth, storage, and edge functions on top. The pieces are open-source, the data is portable, and Postgres is one of the most battle-tested databases in production anywhere. The honest caveats are that the free tier is limited, the row-level security model takes care to design well, and the connection pooler needs explicit configuration for serverless workloads. Once those decisions are made, Supabase comfortably runs a multi-tenant SaaS at the scale most independent UK builders need.

How do you handle multi-tenancy with row-level security?

Every table gets an org_id column and a row-level security policy that ties access to the authenticated user's organisation. Anonymous insert is allowed only on tables that legitimately need it (a waitlist, for example). Service-role keys are kept server-side only, never exposed to the browser. Billing fields get column-level security on top of row-level security so that the client can read its own plan tier without leaking Stripe identifiers. The result is that the database itself enforces tenant isolation, not just the application code, which means a bug in a single API route cannot leak another tenant's data.

What does it cost to run a SaaS at 100 users?

For Semoria's shape, around £55 to £65 a month at 100 paying users. That includes Vercel Pro hosting, Supabase Pro database, Stripe transaction fees (roughly 1.5 percent plus 20p per transaction), Resend for email, PostHog and Sentry on their free or starter tiers, and AI inference costs through OpenRouter. The cost grows with active users, content generations, and AI inference, not with database storage or hosting, so the unit economics stay clean until you hit serious volume.

What did you build versus integrate?

Almost everything that has a well-documented vendor was integrated rather than built. Auth was integrated through Supabase, billing through Stripe, email through Resend, analytics through PostHog, monitoring through Sentry, AI inference through OpenRouter and Perplexity Sonar Pro. What we built ourselves was the voice fingerprinting engine, the content generation pipeline, the publishing workflow, the multi-tenant data model, the marketing engine, and the dashboard. The rule was 'build the product, integrate everything else', which is the only way a single team ships a platform-grade SaaS in six weeks.


Related Articles