# Platform Deep Dive
Everything under the hood — and why it's there.
By AgentSite · Updated 2026-05-23
## The Platform
This isn't a boilerplate. It's a production-grade SaaS foundation built from years of shipping real products — every layer deliberate, every pattern battle-tested. Here's everything under the hood.
* * *
## Architecture Overview
External
Data
Application
Edge
Client
Vue 3 SPA
Service Worker
CDN / Static Assets
Load Balancer
NestJS API Server
WebSocket Gateway
Mastra AI Agents
Scheduled Jobs
PostgreSQL
Redis - Sessions
Stripe Billing
SendGrid Email
Google OAuth
Uploadcare CDN
* * *
## Authentication & Identity
Four auth strategies, zero compromise. Every path leads to a secure JWT session stored in HTTP-only cookies — not localStorage, not session tokens in headers. Cookies. Because the browser's built-in security model is better than anything we'd build.
Protected
Auth Pipeline
Entry Points
Magic Link Email
Password + Argon2
Google OAuth
Ghost Mode
Validate Credentials
Sign JWT
Set HTTP-Only Cookie
Create Session
AuthGuard
Role Check
API Access
**Magic Links** — Passwordless login. User enters email, gets a signed token link, clicks it, done. No password to forget, no credential stuffing to worry about. The token is single-use, time-limited, and tied to the requesting IP.
**Password Auth** — Argon2id hashing. Not bcrypt, not SHA-256. Argon2id — memory-hard, GPU-resistant, winner of the Password Hashing Competition. Password reset flows with signed tokens, not security questions.
**Google OAuth** — One-click sign-in with automatic account linking. If the email already exists, it links. If not, it creates. No friction, no "which account did I use?" confusion.
**Ghost Mode** — Development and demo sessions without real accounts. Perfect for trade shows, investor demos, or automated testing. Full platform access with a synthetic identity that self-destructs.
**Role-Based Access** — Three decorator-driven authorization levels: `@Public()` (no auth), `@RequireAuth()` (any authenticated user), `@RequireEdit()` (admin/editor). Applied at the controller method level. No middleware chains to debug.
* * *
## Database Philosophy
### Flyway Over ORMs
We use Flyway for migrations. Not TypeORM migrations, not Knex migrations, not Prisma migrations. Flyway.
Why? Because database migrations are **SQL**. They should be written in SQL, reviewed as SQL, and executed as SQL. ORM-generated migrations are a leaky abstraction — they guess what you want, generate bloated DDL, and break in ways that are hard to debug.
Guarantees
Migration Pipeline
Developer writes SQL
Versioned file: V01\_\_description.sql
Flyway validates + applies
PostgreSQL
flyway\_schema\_history table
Strict ordering
Checksum verification
Repeatable migrations
Full audit trail
Every migration is a versioned `.sql` file: `V01__create_users.sql`, `V02__add_billing_tables.sql`. Flyway checksums them — if someone modifies a migration that already ran, deployment fails. Loudly. That's a feature.
The result: migrations that are readable, auditable, and identical across every environment. No ORM version drift. No "works on my machine."
### TypeORM for Application Code
TypeORM handles the application layer — entity mapping, query building, repository pattern. But it doesn't touch the schema. Clean separation: SQL owns the structure, TypeORM owns the access.
**Snake case everywhere.** A `NamingStrategy` maps TypeScript's `camelCase` properties to PostgreSQL's `snake_case` columns automatically. No `@Column({ name: 'user_id' })` decorators cluttering entity files.
**Entities are simple.** No active record pattern, no lifecycle hooks on entities, no magic. An entity is a typed row. Services contain the logic.
* * *
## Entity System
has
has
has
has
has
has
references
parent
receives
User
string
id
PK
string
email
string
name
string
role
string
status
Membership
string
id
PK
string
userId
FK
string
orgId
FK
string
role
Session
Profile
Organization
string
id
PK
string
name
string
slug
string
domain
Subscription
BillingCustomer
Plan
Content
string
id
PK
string
slug
string
title
string
type
string
status
jsonb
metadata
text
data
Form
string
id
PK
string
title
jsonb
schema
string
status
FormResponse
Every entity ID is generated with **ashid** — prefixed, collision-resistant, sortable identifiers. `usr_abc123`, `org_def456`, `cnt_ghi789`. You can look at an ID and know what it is. Grep a log file and find every user mention. No UUIDs, no auto-incrementing integers leaking your row count.
* * *
## Billing & Subscriptions
Full Stripe integration — not a wrapper, not a "billing microservice." Direct Stripe API usage with webhook-driven state management.
**Checkout Sessions** — Not custom payment forms. Stripe Checkout handles PCI compliance, 3D Secure, Apple Pay, Google Pay, and every edge case we'd never think to handle.
**Webhook-Driven** — Subscription state is never polled. Stripe tells us when something changes. `invoice.paid`, `customer.subscription.updated`, `customer.subscription.deleted` — each one triggers precise state transitions.
**Plan Enforcement** — Middleware checks subscription status on protected routes. Expired? Graceful degradation, not a brick wall. Usage limits? Tracked per-organization with clear upgrade paths.
* * *
## Admin Dashboard
A full admin panel at `/admin` with granular control over every entity in the system. Not a bolted-on afterthought — it's built with the same component library (NaiveUI) and shares the same API layer.
**What's in the admin:**
- **User Management** — Search, filter, view activity, impersonate, suspend, change roles
- **Organization Management** — View members, subscription status, usage metrics
- **Content Editor** — WYSIWYG markdown editing with live preview, draft/publish workflow
- **Form Builder** — Dynamic form creation with schema-driven fields
- **Billing Overview** — Subscription status across all organizations, revenue metrics
- **System Health** — Active sessions, error rates, webhook delivery status
Every admin action is audit-logged. Who did what, when, to what entity. Not optional, not configurable — always on.
* * *
## Content Management System
A file-based CMS that syncs markdown files to the database on startup. No headless CMS vendor, no API rate limits, no monthly fees.
flowchart LR subgraph File System MD1\[content/about.md\] MD2\[content/blog/post-1.md\] MD3\[content/platform.md\] end subgraph ContentSyncService PARSE\[Parse YAML Frontmatter\] MERGE\[Date-Based Merge\] SYNC\[Upsert to DB\] end subgraph Database CT\[(Content Table)\] end subgraph Delivery API\[GET /content/:slug\] VUE\[Markdown Component\] MERMAID\[Mermaid Renderer\] end MD1 --> PARSE MD2 --> PARSE MD3 --> PARSE PARSE --> MERGE --> SYNC --> CT CT --> API --> VUE --> MERMAID
**Date-Based Merge** — File wins only if its date is newer than the database record. Edit content in the admin panel? Those changes stick until the file is explicitly updated. No accidental overwrites.
**YAML Frontmatter** — Standard metadata: slug, title, type, status, date. Arbitrary metadata in a nested object for flexibility.
**Mermaid Diagrams** — Fenced code blocks with `mermaid` language tags render as interactive SVG diagrams. Architecture charts, flow diagrams, sequence diagrams — all in markdown.
**Admin Editing** — Content synced from files can also be edited through the admin CMS. The merge strategy ensures neither side clobbers the other.
* * *
## Frontend Architecture
### Component System
Vue 3 with TypeScript, class-based components via `vue-facing-decorator`, and auto-imported components via `unplugin-vue-components`. Write a component, use it anywhere — no import statements needed.
graph TB subgraph Build Pipeline VITE\[Vite Dev Server\] TW\[Tailwind CSS v4\] AUTO\[Auto Component Import\] SVG\[SVG Loader\] PAGES\[File-Based Routing\] LAYOUTS\[Layout System\] end subgraph Component Layers NAIVE\[NaiveUI Primitives\] SHARED\[Shared Components\] PAGE\[Page Components\] end subgraph Patterns DEC\[Class Decorators\] PROPS\[@Prop / @Watch\] EMIT\[@Emit\] API\[ApiCore Composable\] end VITE --> TW VITE --> AUTO --> NAIVE VITE --> SVG VITE --> PAGES VITE --> LAYOUTS NAIVE --> SHARED --> PAGE DEC --> PROPS --> EMIT --> API
### File-Based Routing
No router configuration files. Drop a `.vue` file in `src/pages/`, it becomes a route. `pages/about/index.vue` → `/about`. `pages/settings/billing.vue` → `/settings/billing`. Nested layouts, dynamic segments, route meta — all from file conventions.
### Design Token System
CSS custom properties mirrored in TypeScript. Change a color in one place, it updates across CSS, Tailwind utilities, NaiveUI theme overrides, and Storybook simultaneously.
```typescript
// design-tokens.ts — single source of truth
export const colors = {
brand: {
primary: 'var(--color-brand-primary)', // #11203C
accent: 'var(--color-brand-accent)', // #DDDAC0
secondary: 'var(--color-brand-secondary)', // #0e7490
},
ui: {
bgPrimary: 'var(--ui-bg-primary)', // #09090b
textPrimary: 'var(--ui-text-primary)', // #fafafa
}
} as const
```
### Tailwind CSS v4
No `tailwind.config.js`. Tailwind v4 uses CSS-native configuration with `@theme` blocks. Custom utilities defined with `@utility`. A Vite plugin auto-injects `@reference` directives into Vue style blocks — Tailwind utilities work everywhere without manual imports.
### Rich Text Editing
Tiptap editor with markdown import/export via `tiptap-markdown`. Supports images (Uploadcare CDN), links, text alignment, subscript/superscript, character counts, and placeholder text. Content stored as markdown, rendered as HTML.
* * *
## API Layer
### Object-Oriented First
Every service is a class. Every controller is a class. Dependency injection via NestJS's IoC container. No scattered utility functions, no "just import this helper." Services have clear boundaries, single responsibilities, and injectable dependencies.
graph LR subgraph Controllers UC\[UserController\] OC\[OrgController\] CC\[ContentController\] BC\[BillingController\] FC\[FormController\] end subgraph Services US\[UserService\] OS\[OrgService\] CS\[ContentService\] BS\[BillingService\] FS\[FormService\] ES\[EmailService\] TS\[TrackingService\] end subgraph Cross-Cutting AUTH\[AuthGuard\] LOG\[LoggDecorator\] VALID\[ValidationPipe\] ERR\[ExceptionFilter\] end UC --> US OC --> OS CC --> CS BC --> BS FC --> FS US --> ES BS --> ES US --> TS AUTH --> UC & OC & CC & BC & FC LOG --> US & OS & CS & BS & FS
### Structured Logging
The `@Logged()` decorator wraps any service method with structured logging — method name, duration, parameters, result flags. Not `console.log`. Not a logging library you have to configure. A decorator that makes every service method observable with zero boilerplate.
### API Client Pattern
Frontend API calls go through `ApiCore` — a typed Axios wrapper that handles auth headers, error normalization, pagination, and response unwrapping. One import, consistent behavior everywhere:
```typescript
const users = await api.get<User[]>('/user')
const user = await api.post<User>('/user', { name: 'Alice' })
```
* * *
## Real-Time Communication
### WebSocket Gateway
Socket.IO-based real-time channel. Authenticated connections tied to user sessions. Server-pushed events for notifications, live updates, and AI streaming responses.
### AI Agent Integration
Mastra framework for AI agent orchestration. Agents stream responses over WebSocket — not polling, not long-polling. The user sees tokens appear as they're generated.
sequenceDiagram participant U as User participant WS as WebSocket participant M as Mastra Agent participant LLM as Language Model U->>WS: Send prompt WS->>M: Execute agent M->>LLM: Stream request loop Token streaming LLM-->>M: Token M-->>WS: Push token WS-->>U: Render token end M-->>WS: Complete WS-->>U: Done
* * *
## DevOps & Deployment
### CI/CD Pipeline
GitHub Actions with a two-stage pipeline: test everything, then deploy. No manual steps, no "just SSH in and restart."
flowchart LR subgraph CI PUSH\[Git Push\] TEST\[Run Tests\] LINT\[Type Check\] BUILD\[Build Images\] end subgraph CD REG\[Push to Registry\] K8S\[Apply K8s Manifests\] MIGRATE\[Run Flyway Migrations\] DEPLOY\[Rolling Update\] HEALTH\[Health Check\] end PUSH --> TEST --> LINT --> BUILD BUILD --> REG --> K8S --> MIGRATE --> DEPLOY --> HEALTH
### Kubernetes Deployment
Production runs on Kubernetes with:
- **Rolling updates** — Zero-downtime deployments. New pods spin up, health checks pass, old pods drain.
- **Resource limits** — CPU and memory bounds per container. No runaway processes eating the node.
- **ConfigMaps & Secrets** — Environment configuration without baking values into images.
- **DigitalOcean Container Registry** — Private image storage. Images tagged with commit SHA for traceability.
### DevOps Scripts
A comprehensive `devops/scripts/` toolkit:
- **`start-backend.sh`** — Runs Flyway migrations, then starts NestJS. Migrations always run first. Always.
- **`local-docker-compose.sh`** — Full local stack: PostgreSQL, Redis, backend, frontend. One command.
- **`generate-dev-env-js.sh`** — Injects environment variables into the frontend at build time without rebuilding.
- **`repo-discovery.sh`** — Finds sibling projects using this template. Powers the update-child sync workflow.
- **`util-diff.sh`** — Diffs a child project against the template. Shows what's customized vs. what can be synced.
* * *
## Testing Infrastructure
### Unit Tests
Jest with TypeScript via `ts-jest`. Backend services tested with real database connections — not mocks. When a test passes, it means the SQL actually works against a real PostgreSQL instance.
### End-to-End Tests
Playwright for browser automation. Full user journey tests: sign up, create content, manage billing, admin workflows. Tests run in CI against a deployed preview environment.
### Storybook
Component development and visual testing with Storybook 10. NaiveUI theme integration, accessibility auditing (`@storybook/addon-a11y`), dark/light theme switching. Every shared component has a story.
* * *
## Form System
Dynamic form builder with schema-driven rendering and AI-powered question generation.
flowchart TD subgraph Builder SCHEMA\[JSON Schema Definition\] FIELDS\[Field Types: text, select, rating, etc.\] VALID\[Validation Rules\] end subgraph Runtime RENDER\[Dynamic Renderer\] COLLECT\[Response Collection\] STORE\[FormResponse Entity\] end subgraph AI Optional TAG\[Tag-Based Context\] GEN\[AI Question Generation\] ADAPT\[Adaptive Follow-ups\] end SCHEMA --> FIELDS --> VALID --> RENDER RENDER --> COLLECT --> STORE TAG --> GEN --> ADAPT --> RENDER
Forms are stored as JSONB schemas — not hardcoded templates. Create a form through the admin, it renders automatically. Add validation rules, they enforce automatically. The entire form lifecycle is data-driven.
* * *
## Email System
SendGrid integration with template-based transactional emails. Not raw HTML strings concatenated in a service method.
- **Magic link login** — Branded email with single-use auth link
- **Password reset** — Time-limited token with clear expiration messaging
- **Welcome emails** — Post-registration onboarding
- **Notification digests** — Batched updates, not individual spam
Email templates are maintainable HTML with variable interpolation. Preview them locally before deploying.
* * *
## Tracking & Analytics
PostHog integration for product analytics with a backend `TrackingService` for server-side events. Client-side page views and interactions. Server-side conversion events, API usage metrics, and funnel tracking.
Events are typed — not arbitrary strings. A `TrackingEvent` enum ensures consistency across the codebase. No "did we call it `user_signup` or `userSignup` or `sign_up`?" confusion.
* * *
## Protocol System
Knowledge management through protocol files — executable documentation that lives alongside the code.
graph TB subgraph Protocol Types PROC\[Procedures - How to do things\] IMPL\[Implementations - How to build things\] REF\[References - Where to find things\] end subgraph Lifecycle WRITE\[Write Protocol\] PUBLISH\[Publish to API\] FETCH\[Fetch On-Demand\] EXECUTE\[Follow Steps\] end subgraph Examples SETUP\["@codebase/setup"\] UPDATE\["@codebase/update-parent"\] FEATURE\["@codebase/feature/\*"\] INNOV\["@codebase/innovations"\] end PROC --> WRITE IMPL --> WRITE REF --> WRITE WRITE --> PUBLISH --> FETCH --> EXECUTE SETUP --> PROC UPDATE --> PROC FEATURE --> IMPL INNOV --> REF
Protocols are published to an API and fetched on demand. They're not stale wiki pages — they're versioned, searchable, and used by both humans and AI agents. When you bootstrap a session, you load the protocols that matter. When you need to do something, you fetch the procedure.
* * *
## What This Adds Up To
This isn't a starter template you'll outgrow in a month. It's the infrastructure layer of a real SaaS product — authentication, billing, content, email, real-time, AI, admin, deployment, testing, and documentation. All integrated. All production-ready. All yours to build on.
Every pattern here exists because we hit the wall without it. Every architectural decision has a scar behind it. This is what years of shipping looks like, compressed into a single `git clone`.
* * *
## Further reading
The architecture above implements the technical layers of [agent readability](/agent-readability). The five-layer model the system delivers against is documented in [the five layers of AEO](/five-layer-aeo). The specific Layer-1 failure modes that drive most install conversations are catalogued in [SSR-junk and bot walls](/ssr-junk-bot-wall).