AI context file for Claude Code and other AI assistants.
Open-source SaaS boilerplate built with Vue 3 + Quasar. Ships for web, iOS, Android, desktop (Electron), and PWA from a single codebase. Includes Directus CMS, Stripe billing, authentication, analytics, and AI copilot capabilities.
Repository: https://github.com/manicinc/synthstack License: MIT License - see LICENSE
| Layer | Technology |
|---|---|
| Frontend | Vue 3, Quasar 2.x, TypeScript, Pinia |
| Backend | Fastify, Node.js 20+ |
| CMS | Directus 11.x |
| Database | PostgreSQL 15+ |
| Cache | Redis 7+ |
| Vectors | Qdrant (for RAG/semantic search) |
| Payments | Stripe |
| Auth | Supabase or Local PostgreSQL |
| AI | OpenAI/Anthropic SDKs (BYOK model) |
synthstack/
├── apps/
│ └── web/ # Vue 3 + Quasar frontend
│ ├── src/
│ │ ├── boot/ # App initialization (Supabase, Stripe, i18n)
│ │ ├── components/ # Reusable Vue components
│ │ ├── composables/ # Vue composition hooks (useApi, etc.)
│ │ ├── layouts/ # Page layouts (AppLayout, AuthLayout)
│ │ ├── pages/ # Route pages
│ │ ├── router/ # Vue Router config
│ │ ├── services/ # API clients & business logic
│ │ ├── stores/ # Pinia state management
│ │ └── types/ # TypeScript definitions
│ ├── e2e/ # Playwright E2E tests
│ └── test/ # Vitest unit tests
├── packages/
│ ├── api-gateway/ # Fastify REST API
│ │ └── src/
│ │ ├── routes/ # API endpoints
│ │ ├── services/ # Business logic
│ │ └── plugins/ # Fastify plugins
│ ├── types/ # Shared TypeScript types (@synthstack/types)
│ ├── ts-ml-service/ # TypeScript ML service (NestJS)
│ └── directus-extension-synthstack/ # CMS extensions
├── services/
│ └── directus/ # Directus CMS configuration
├── docs/ # Documentation (60+ files)
├── deploy/ # Deployment scripts and configs
├── config.json # Branding & infrastructure config
├── docker-compose.community.yml # Docker environment
└── turbo.json # Turborepo config
# Install dependencies
pnpm install
# Start Docker services (Postgres, Redis, Qdrant, Directus)
docker compose -f docker-compose.community.yml up -d
# Start frontend dev server (localhost:3050)
pnpm dev:web
# Start API dev server (localhost:3003)
pnpm dev:api
# Run all tests
pnpm test
# Run E2E tests
pnpm test:e2e
# Lint and type check
pnpm lint && pnpm typecheck
# Build for production
pnpm buildAll components use <script setup lang="ts"> with Composition API:
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useApi } from 'src/composables/useApi'
const props = defineProps<{ id: string }>()
const emit = defineEmits<{ update: [value: string] }>()
const { data, loading, error } = useApi(`/endpoint/${props.id}`)
</script>Stores in apps/web/src/stores/ use composition API pattern:
export const useUserStore = defineStore('user', () => {
const user = ref<User | null>(null)
const isLoggedIn = computed(() => !!user.value)
async function fetchUser() { /* ... */ }
return { user, isLoggedIn, fetchUser }
})Routes in packages/api-gateway/src/routes/:
export default async function routes(fastify: FastifyInstance) {
fastify.get<{ Params: { id: string } }>(
'/:id',
{ schema: { params: ParamsSchema, response: { 200: ResponseSchema } } },
async (request, reply) => {
// Handler
}
)
}Features controlled via environment variables (.env):
ENABLE_COPILOT=true # AI chat assistant
ENABLE_COPILOT_RAG=false # Semantic document search (Pro)
ENABLE_AI_AGENTS=false # LangGraph agents (Pro)
ENABLE_REFERRALS=false # Referral system (Pro)Check flags in code:
import { useFeatureStore } from 'src/stores/features'
const features = useFeatureStore()
if (features.copilotEnabled) { /* ... */ }- Copy
.env.exampleto.env(repo root is the single source of truth) - Required variables:
SUPABASE_URL,SUPABASE_ANON_KEY- Auth (or use local auth)STRIPE_SECRET_KEY,VITE_STRIPE_PUBLISHABLE_KEY- PaymentsOPENAI_API_KEYorANTHROPIC_API_KEY- AI features
See docs/ENVIRONMENT_SETUP.md for complete variable reference.
| Type | Command | Location |
|---|---|---|
| Unit | pnpm test |
apps/web/src/**/__tests__/ |
| E2E | pnpm test:e2e |
apps/web/e2e/ |
| API | pnpm test:api |
packages/api-gateway/src/**/__tests__/ |
Test files are colocated with source files in __tests__/ directories.
- Always run
pnpm typecheckbefore committing - TypeScript strict mode is enabled - Never commit
.envfiles - Only.env.*.examplefiles are safe - Feature flags control edition differences - Check
ENABLE_*vars before adding Pro features - Tests are required for new features - Both unit and E2E where applicable
- Use existing composables - Check
src/composables/before creating new patterns - Follow Quasar conventions - Use Quasar components over raw HTML
| Service | URL |
|---|---|
| Frontend | http://localhost:3050 |
| API Gateway | http://localhost:3003 |
| API Docs (Swagger) | http://localhost:3003/docs |
| Directus CMS | http://localhost:8099/admin |
| Qdrant Dashboard | http://localhost:6333/dashboard |
Key docs to reference:
docs/QUICK_START.md- Get running in 5 minutesdocs/ENVIRONMENT_SETUP.md- All environment variablesdocs/features/COPILOT.md- AI copilot architecturedocs/features/STRIPE_INTEGRATION.md- Billing setupdocs/AUTHENTICATION.md- Auth provider configurationdocs/REBRANDING_GUIDE.md- How to rebrand the app
To rebrand for your SaaS:
- Update
config.json(app name, colors, company info) - Replace logos in
apps/web/public/logo/ - Update environment variables
- See
docs/REBRANDING_GUIDE.mdfor complete guide
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web SPA │ │ iOS/Android │ │ Electron │ │
│ │ (Quasar) │ │ (Capacitor) │ │ (Desktop) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ └────────────────┴────────────────┘ │
│ │ │
│ Vue 3 + Pinia + Vue Router + TypeScript │
└──────────────────────────┼──────────────────────────────────────┘
│ HTTP/WebSocket
┌──────────────────────────┼──────────────────────────────────────┐
│ API GATEWAY │
│ ┌───────────────────────┴───────────────────────┐ │
│ │ Fastify Server │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Routes │ │ Services│ │Middleware│ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ └───────────┴───────────┘ │ │
│ └───────────────────────────────────────────────┘ │
└──────────────────────────┼──────────────────────────────────────┘
│
┌──────────────────────────┼──────────────────────────────────────┐
│ DATA LAYER │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Postgres │ │ Redis │ │ Qdrant │ │ Directus │ │
│ │ (DB) │ │ (Cache) │ │(Vectors) │ │ (CMS) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
User Action → Vue Component → Pinia Store → API Service → API Gateway → Database
↑ │
└────────── Response ────────┘
Example: User updates profile
ProfilePage.vuecallsuserStore.updateProfile(data)- Store calls
api.patch('/users/me', data) - API Gateway validates, updates DB, returns result
- Store updates reactive state
- Component re-renders automatically
-
Import from node_modules paths directly
// BAD import { something } from '../../../node_modules/package' // GOOD import { something } from 'package'
-
Use Options API in new components
// BAD - Options API export default { data() { return {} }, methods: {} } // GOOD - Composition API <script setup lang="ts">
-
Duplicate API logic in components
// BAD - API call in component const response = await fetch('/api/users') // GOOD - Use composable or service const { data } = useApi('/users')
-
Hardcode feature availability
// BAD if (true) { showProFeature() } // GOOD if (features.copilotEnabled) { showProFeature() }
-
Store secrets in code
// BAD const API_KEY = 'sk-12345...' // GOOD const API_KEY = import.meta.env.VITE_API_KEY
-
Skip TypeScript types
// BAD const data: any = await fetchData() // GOOD const data: UserProfile = await fetchData()
-
Create new patterns when existing ones exist
- Check
src/composables/before creating hooks - Check
src/services/before creating API clients - Check
src/stores/before creating global state
- Check
| To find... | Look in... |
|---|---|
| Landing page | apps/web/src/pages/LandingPage.vue |
| Dashboard | apps/web/src/pages/app/DashboardPage.vue |
| Auth pages | apps/web/src/pages/auth/ |
| API routes | packages/api-gateway/src/routes/ |
| Pinia stores | apps/web/src/stores/ |
| Composables | apps/web/src/composables/ |
| API service | apps/web/src/services/api.ts |
| Auth service | apps/web/src/services/auth.ts |
| Feature flags | apps/web/src/config/features.ts |
| Branding config | config.json (root) |
| Environment vars | .env.*.example files |
| Shared types | packages/types/src/ |
| Vue Router | apps/web/src/router/routes.ts |
| Layouts | apps/web/src/layouts/ |
| i18n translations | apps/web/src/i18n/ |
- Create
apps/web/src/pages/app/NewPage.vue - Add route in
apps/web/src/router/routes.tsunder app routes - Add navigation link in relevant layout/menu
- Create route file in
packages/api-gateway/src/routes/ - Register in
packages/api-gateway/src/routes/index.ts - Add types to
packages/types/src/api/if needed - Call from frontend via
src/services/api.ts
- Add to
.env.example - Add to
apps/web/src/config/features.ts - Add to
apps/web/src/stores/features.ts - Use via
useFeatureStore()in components
- Update
config.json(colors, names, URLs) - Replace logos in
apps/web/public/logo/ - Update favicons in
apps/web/public/
- Create
apps/web/src/stores/newStore.ts - Export from
apps/web/src/stores/index.ts - Use via
useNewStore()in components
When working with this codebase:
-
Always check existing patterns first - This codebase has established patterns for components, stores, services, and API routes. Follow them.
-
Run type checks frequently -
pnpm typecheckcatches most issues early. -
Feature flags are critical - Community vs Pro editions are controlled by feature flags. Always check if a feature should be gated.
-
Quasar has many built-ins - Before adding a new UI library, check if Quasar already has the component.
-
Tests are colocated - Look for
__tests__/directories next to source files. -
Config is centralized -
config.jsoncontrols branding,features.tscontrols feature flags. -
Environment is centralized - use repo root
.env(generated from.env.exampleorpnpm generate:env).