Architecture Overview
Contentrain Studio is a conversation-first CMS built as a single Nuxt 4 full-stack application. It is deployment-flexible: the AGPL core is self-hostable, and managed Pro/Enterprise deployments can run on the same architecture. All external services are abstracted through provider interfaces, allowing operators to swap implementations without changing application code.
Studio shares the .contentrain/ contract with the Contentrain AI package surface, but it is not an @contentrain/mcp wrapper. The package side handles local-first execution and IDE workflows; Studio adds the authenticated web layer for collaboration, review, and delivery.
System Overview
+--------------------------+
| Reverse Proxy |
| (TLS termination) |
+------------+-------------+
|
+------------v-------------+
| Contentrain Studio |
| Nuxt 4 / Nitro |
| |
| +-------+ +---------+ |
| | App | | Server | |
| | (SPA) | | (API) | |
| +-------+ +---------+ |
+------|---------|----------+
| |
+------------------+---------+------------------+
| | | | |
+-----v---+ +---v----+ +--v-----+ +--v----+ +--------v-------+
| Auth | | DB | | Git | | AI | | Object Storage |
| Provider| |Provider| |Provider| |Provider| | (CDN/Media) |
+---------+ +--------+ +--------+ +-------+ +----------------+
Supabase Supabase GitHub Anthropic Cloudflare R2
Auth PostgreSQL App Claude (ee/)Nuxt 4 Full-Stack Architecture
Studio is a single project -- not a monorepo. Nuxt 4 runs both the client SPA and the Nitro server in one deployment unit.
studio/
├── app/ # Client-side (SPA, SSR disabled)
│ ├── components/ # Atomic design: atoms, molecules, organisms
│ ├── composables/ # Client-side business logic
│ ├── layouts/ # auth, default, workspace
│ ├── pages/ # File-based routing
│ └── assets/ # CSS, fonts, images
├── server/ # Server-side (Nitro)
│ ├── api/ # REST API endpoints
│ ├── middleware/ # Auth, billing, invite acceptance, audit
│ ├── providers/ # Provider interfaces + implementations
│ └── utils/ # Agent tools, content engine, helpers
├── shared/ # Shared code (client + server)
│ └── utils/ # License/feature flags
├── ee/ # Enterprise Edition (proprietary)
│ ├── cdn/ # Cloudflare R2 CDN provider
│ ├── media/ # Sharp image processing
│ └── enterprise/ # Bridge + route handlers
└── .contentrain/ # Content directory (UI strings, agent prompts)Key Configuration
SSR is disabled (ssr: false) because Studio is an authenticated app with no public marketing pages. The client runs as a SPA behind the auth gate.
// nuxt.config.ts
export default defineNuxtConfig({
ssr: false,
modules: ['@nuxt/eslint', '@nuxt/image'],
css: ['~/assets/css/main.css'],
// ...
})Provider / Adapter Pattern
This is the most critical architectural decision in Studio. No implementation detail ever leaks into components, pages, composables, or server routes.
How It Works
- Interfaces define contracts in
server/providers/:
// server/providers/auth.ts
export interface AuthProvider {
validateToken: (accessToken: string) => Promise<AuthUser | null>
refreshSession: (refreshToken: string) => Promise<AuthTokens | null>
getOAuthRedirectUrl: (provider: 'github' | 'google', redirectTo: string) => Promise<OAuthRedirectResult>
exchangeCode: (code: string, state?: string) => Promise<AuthSession>
sendMagicLink: (email: string, redirectTo: string) => Promise<void>
deleteUser: (userId: string) => Promise<void>
// ...
}- Implementations live alongside interfaces (or in
ee/):
// server/providers/supabase-auth.ts
export function createSupabaseAuthProvider(): AuthProvider {
// Uses @supabase/supabase-js internally
// This is the ONLY file that imports Supabase client code
}- Factories resolve singletons via
server/utils/providers.ts:
// server/utils/providers.ts
export function useAuthProvider(): AuthProvider {
if (!_authProvider)
_authProvider = createSupabaseAuthProvider()
return _authProvider
}- Application code only imports from factories:
// In any server route
const auth = useAuthProvider()
const user = await auth.validateToken(token)Provider Inventory
| Provider | Interface | Current Implementation | Singleton? |
|---|---|---|---|
| Auth | AuthProvider | Supabase Auth | Yes |
| Database | DatabaseProvider | Supabase PostgreSQL | Yes |
| Git | GitProvider | GitHub App | No (per-repo) |
| Git App | GitAppProvider | GitHub App Installation | No (per-install) |
| AI | AIProvider | Anthropic Claude | Yes |
| CDN | CDNProvider | Cloudflare R2 (ee/) | Yes |
| Media | MediaProvider | Sharp + R2 (ee/) | Yes |
EmailProvider | Resend | Yes | |
| Payment | PaymentProvider | Stripe | Yes |
| Connector | ConnectorProvider | None yet | N/A |
Rules -- Never Violate
- NEVER import
@supabase/supabase-jsoutside ofserver/providers/ - NEVER use
useSupabaseClient()oruseSupabaseUser()in components - ALL auth checks use
AuthProvider.getSession(), never Supabase directly - Provider instances are resolved via factory functions only
Workspace Hierarchy
Studio organizes data in a three-level hierarchy:
User
└── Workspace (billing entity)
├── Members (owner, admin, member)
├── GitHub App Installation
└── Projects (connected repositories)
├── Members (editor, reviewer, viewer)
├── Conversations
├── Content (via Git)
└── Media AssetsKey Rules
- Signup auto-creates a primary workspace (type:
primary) - Each workspace has exactly one owner (the GitHub-authenticated user)
- GitHub App installation lives on the workspace, not the project
- Workspace Owner/Admin has implicit access to all projects
- Workspace Member needs explicit
project_membersassignment
Component Architecture
Studio follows atomic design principles, adapted for Radix Vue + Tailwind CSS 4:
app/components/
├── atoms/ # Radix Vue primitives + Tailwind
│ ├── HeadingText.vue
│ ├── BaseButton.vue
│ ├── FormInput.vue
│ ├── FormLabel.vue
│ ├── Badge.vue
│ └── Avatar.vue
├── molecules/ # Composed atoms
│ ├── ProviderButtons.vue
│ ├── AuthLink.vue
│ └── EmailButton.vue
└── organisms/ # Business logic components
├── SigninWithProvider.vue
├── ProfileOverviewPanel.vue
└── WorkspaceMembersPanel.vueThere is no templates/ layer -- Nuxt layouts (app/layouts/) handle page-level wrappers.
Layout System
| Layout | Purpose | Structure |
|---|---|---|
auth | Login/callback pages | Split panel (form left, marketing right) |
default | Workspace list, settings | Sidebar (240px) + main content |
workspace | Project workspace | Three-panel (sidebar 240px, chat, context 400px) |
Data Flow
Authentication Flow
Client Server Supabase
| | |
|-- POST /api/auth/login -| |
| |-- getOAuthRedirectUrl() --|
|<- { url } -------------| |
| | |
|-- (OAuth redirect) -----|-------------------------->|
|<- (callback with code) -| |
| | |
|-- POST /api/auth/verify | |
| |-- exchangeCode() -------->|
| |<- session + tokens -------|
| |-- setServerSession() -----|
|<- { user } ------------| |Content Operation Flow
User Chat Message
|
v
Intent Classification
|
v
System Prompt Construction
(config, models, permissions, UI context, vocabulary)
|
v
AI Provider (streaming)
|
+--> Tool Call (save_content, save_model, etc.)
| |
| v
| State Machine Guard
| |
| v
| Content Engine
| |
| v
| GitProvider (branch -> commit -> merge)
| |
| v
| Brain Cache Invalidation
|
v
SSE Events -> Client UIKey Design Decisions
Session Management
Sessions use AES-256 encrypted httpOnly cookies managed by h3 useSession(). The auth middleware automatically refreshes tokens 5 minutes before expiry.
Git-Based Content
All content is stored in Git repositories using the .contentrain/ directory format. Studio never stores content in its database -- Git is the single source of truth. The contentrain branch serves as the SSOT branch, with cr/* feature branches for individual operations.
Enterprise Bridge
Enterprise features (ee/) are loaded at runtime via a dynamic import bridge pattern. If ee/ is absent, the bridge returns null and features degrade gracefully (returning 403 for EE-only routes).
Feature Flags
All feature gating uses hasFeature(plan, 'feature.name'). Plan checks are never hardcoded -- the FEATURE_MATRIX in shared/utils/license.ts is the single source of truth.
Related Pages
- Provider Interfaces -- full interface definitions
- Content Engine -- write path internals
- Agent System -- AI chat architecture
- Enterprise Edition -- open-core model details
- Roles & Permissions -- two-tier role system