- π¨ Sanity-inspired admin - Responsive 3-panel editor with mobile navigation, auto-save, validation, version history, and visual preview
- π Database adapters - PostgreSQL, embedded Postgres via PGlite, and SQLite/libsql with a shared
DatabaseAdaptercontract - βοΈ Storage flexible - Local filesystem or S3-compatible storage (R2, AWS S3, MinIO)
- π Auth agnostic - Bring your own auth; Better Auth integration ships with sessions, organizations, invitations, and API keys
- π Type-safe schemas - Define content models in TypeScript and generate strongly typed Local API collections
- βοΈ Portable Text rich content - TipTap-backed block editor with custom blocks, inline objects, marks, and annotations
- π Draft/publish workflow - Auto-save, hash-based change detection, publish/unpublish, and rolling version history
- ποΈ Visual editing - Live preview with stega-encoded click-to-edit overlays via
@aphexcms/visual-editing - π’ Multi-tenancy - Organizations, parent/child hierarchy, capability RBAC, field-level access, and Postgres RLS
- π API keys - Org-scoped programmatic access with rate limiting, read/write scopes, and fine-grained capability allowlists
- π Built-in APIs - Local API, Zod-validated HTTP API, generated GraphQL, and Streamable HTTP MCP server
- π Reference resolution - Nested depth control, circular protection, and publish guards for referenced content
| Package | Description |
|---|---|
@aphexcms/cms-core |
Database-agnostic core engine with admin UI, API handlers, and built-in GraphQL |
@aphexcms/postgresql-adapter |
PostgreSQL implementation with Drizzle ORM |
@aphexcms/sqlite-adapter |
SQLite/libsql implementation (local file: databases and Turso) |
@aphexcms/storage-s3 |
S3-compatible storage (R2, AWS S3, MinIO, etc.) |
@aphexcms/nodemailer-adapter |
Nodemailer/SMTP email adapter (with Mailpit helper for local dev) |
@aphexcms/resend-adapter |
Resend API email adapter for production |
@aphexcms/ui |
Shared shadcn-svelte component library |
@aphexcms/visual-editing |
Live preview overlay, stega helpers, and click-to-edit frontend integration |
@aphexcms/base |
Starter template scaffolded by create-aphex |
@aphexcms/blog |
Blog template with public frontend and visual editing examples |
@aphexcms/studio |
Reference implementation app (drives the template) |
create-aphex |
Scaffolder invoked by pnpm create aphex / npm create aphex@latest |
π‘ Architecture deep-dive: See ARCHITECTURE.md for detailed design patterns and internals.
π‘ Adding UI components: Run
pnpm shadcn <component-name>to add shadcn-svelte components to@aphexcms/ui
The fastest way to get started:
pnpm create aphex my-app
# or
npm create aphex my-app
# or
npx create-aphex my-appThis will:
- Prompt you for a project name
- Scaffold a full Aphex CMS project
- Generate a
.envfile with all required environment variables - Provide next steps for starting your project
Then:
cd your-project-name
pnpm install
pnpm db:start # Start PostgreSQL via Docker
pnpm db:push # Push database schema
pnpm dev # Start development serverPrefer no Docker for local development? Use APHEX_DATABASE=sqlite for a local libsql file database, or APHEX_DATABASE=pglite for embedded Postgres semantics.
π Admin UI: http://localhost:5173/admin
If you want to contribute to Aphex or work with the monorepo:
git clone https://github.com/IcelandicIcecream/aphex.git
cd aphex
pnpm installThen pick a database.
The fastest path. The schema is pushed on boot, so there's nothing to run but the dev server:
cd apps/studio
cp .env.example .env
echo 'APHEX_DATABASE=sqlite' >> .env
cd ../..
pnpm devThat's it. The database is created at apps/studio/.aphex/studio.db (gitignored). Email verification is off by default, so the first account you create can sign in immediately β no SMTP server needed.
Needs Docker, and an explicit migration step:
cd apps/studio
cp .env.example .env # default connection string works locally
cd ../..
pnpm db:start # Postgres + Mailpit via Docker
pnpm db:migrate
pnpm devPrefer Postgres semantics without Docker? Set
APHEX_DATABASE=pglitefor an embedded Postgres persisted to a local folder, then runaphex migrateonce beforepnpm dev.
π Admin UI: http://localhost:5173/admin β the first user to sign up becomes super admin.
By default, uses local filesystem. For cloud storage:
pnpm add @aphexcms/storage-s3// apps/studio/src/lib/server/storage/index.ts
import { s3Storage } from '@aphexcms/storage-s3';
export const storageAdapter = s3Storage({
bucket: env.R2_BUCKET,
endpoint: env.R2_ENDPOINT,
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
publicUrl: env.R2_PUBLIC_URL
}).adapter;// aphex.config.ts
import { storageAdapter } from './src/lib/server/storage';
export default createCMSConfig({
storage: storageAdapter // Pass your adapter
});Content models live in your app as TypeScript objects:
// apps/studio/src/lib/schemaTypes/page.ts
export const page: SchemaType = {
name: 'page',
type: 'document',
title: 'Page',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
validation: (Rule) => Rule.required().max(100)
},
{
name: 'slug',
type: 'slug',
title: 'URL Slug',
source: 'title', // Auto-generate from title
validation: (Rule) => Rule.required()
},
{
name: 'content',
type: 'array',
title: 'Content Blocks',
of: [{ type: 'textBlock' }, { type: 'imageBlock' }, { type: 'catalogBlock' }]
},
{
name: 'author',
type: 'reference',
title: 'Author',
to: [{ type: 'author' }] // Reference to other documents
}
]
};Register schemas in your config:
// aphex.config.ts
import { page, author, textBlock } from './src/lib/schemaTypes';
export default createCMSConfig({
schemaTypes: [page, author, textBlock]
// ...
});Available field types: string, text, number, boolean, slug, url, date, datetime, image, file, array, object, reference. Rich text uses Portable Text block arrays: { type: 'array', of: [{ type: 'block' }] }.
- SvelteKit V2 - Framework with Svelte 5 runes
- Drizzle ORM - Type-safe database queries
- Better Auth - Authentication & sessions
- Turborepo - Monorepo build system
- Tailwind CSS v4 - Styling
- shadcn-svelte - UI components
The admin UI is a responsive 3-panel layout inspired by Sanity Studio:
- Desktop: Side-by-side panels (types β documents β editor)
- Mobile: Stack navigation with breadcrumbs
- Real-time validation with inline error messages
- Auto-save every 2 seconds (never lose work!)
- Draft/publish/version history with preview and restore
- Visual editing when a schema defines
previewUrl - Nested reference editing via modal overlays
- Drag-and-drop array field reordering
# Just IDs (default)
GET /api/documents/123
# Resolve first-level references
GET /api/documents/123?depth=1
# Resolve nested references
GET /api/documents/123?depth=2Circular reference protection prevents infinite loops. Max depth: 5.
GraphQL is built into cms-core and enabled by default. To customize:
export default createCMSConfig({
graphql: {
path: '/api/graphql',
enableGraphiQL: true
}
});Visit /api/graphql for GraphiQL interface with auto-generated schema.
Aphex ships a Streamable HTTP MCP server for AI clients such as Claude Code and Cursor. Scaffolded apps expose it at /mcp with a one-line route re-export:
export { POST, GET, DELETE } from '@aphexcms/cms-core/routes/mcp';Authenticate with an org-scoped API key:
claude mcp add --transport http aphex http://localhost:5173/mcp \
--header "x-api-key: your-api-key-here"Current tools cover schema inspection, validation, document query/create/update/publish, singleton reads/writes, and asset listing.
# Development
pnpm dev # Start all packages in watch mode
pnpm dev:studio # Start studio app only
pnpm dev:package # Start cms-core package only
pnpm dev:docs # Start dev server
# Building
pnpm build # Build all packages (Turborepo)
pnpm preview # Preview production build
# Database
pnpm db:start # Start PostgreSQL (Docker)
pnpm db:push # Push schema changes (dev)
pnpm db:generate # Generate migrations
pnpm db:migrate # Run migrations (prod)
pnpm db:studio # Open Drizzle Studio
# Code Quality
pnpm lint # Prettier + ESLint check
pnpm format # Format code with Prettier
pnpm check # Type-check all packages
# UI Components (shadcn-svelte β @aphexcms/ui)
pnpm shadcn button # Add button component
pnpm shadcn dialog # Add dialog component
# Components shared between cms-core & studioBatteries included with Better Auth:
- β Session-based auth (email/password)
- β API keys with rate limiting (10k requests/day)
- β Multi-tenancy with organizations
- β Row-Level Security (RLS)
curl http://localhost:5173/api/documents?docType=page \
-H "x-api-key: your-api-key-here"Generate keys from /admin/settings.
Bring your own auth: Implement the
AuthProviderinterface to use Auth.js, Lucia, or custom solutions.
- β
Format before committing:
pnpm format - β
Type-check:
pnpm check - β
Use Svelte 5 runes (
$state,$derived,$effect) - β Follow Conventional Commits
- Database Adapters: Implement
DatabaseAdapterinterface in a new package + AuthProvider - Storage Adapters: Implement
StorageAdapterinterface - Field Types: Add Svelte component + TypeScript type
- Custom API routes: Register Hono routes/middleware through the
api(app)config hook - Plugins: A first-class plugin API is planned; today, use schemas, custom routes, adapters, and app-level Svelte components
See ARCHITECTURE.md for detailed extension guides.
Include:
- OS, Node version, pnpm version
- Steps to reproduce
- Expected vs actual behavior
- Error logs (browser console + terminal)
- ARCHITECTURE.md - Deep dive into design patterns and internals
- CONTRIBUTING.md - Development guidelines and PR process
- CLI scaffolding β
pnpm create aphex/npm create aphex@latest(published ascreate-aphex) - CI/CD pipeline β
release.yml+sync-template.yml+ Changesets - Unified Local/HTTP/GraphQL API β one schema, three surfaces, Zod-validated contracts
- Auto-generated GraphQL β queries, mutations, filters, GraphiQL
- MCP server β Streamable HTTP endpoint with schema, validation, content, singleton, publish, and asset tools
- Draft/published workflow β hash-based change detection + auto-save
- Version history β rolling per-document versions with configurable
maxVersions(GET /api/documents/{id}/versions) - Multi-tenancy β organizations with parent/child hierarchy + Postgres RLS
- Email + invitations β Better Auth + Resend/Nodemailer adapters, Mailpit in dev
- Capability-based access control β editable built-in roles, custom per-org roles, schema-level and field-level access rules, policy functions
- API keys β rate-limited, per-organization, with either coarse
read/writescopes or a fine-grainedcapabilitiesallowlist - In-memory caching β
InMemoryCacheAdapterforpublishedreads + API-key lookups - Preview config β
preview: { select: { title, subtitle } }(with dot-paths) on document + object types; rendered in document list, array item rows, and reference picker - Visual editing β
previewUrl, live preview iframe, stega encoding, click-to-edit overlay, and@aphexcms/visual-editing - Rich text / block editor β Portable Text model with TipTap editor, built-in image blocks, custom block types, inline objects, marks, and annotations
- Singletons β schemas marked
singleton: trueexpose aSingletonCollection<T>surface withget/update/getSingletonIdand hide Create/Delete in admin - PostgreSQL, PGlite, and SQLite adapters β Docker Postgres, embedded Postgres, local
file:SQLite, and Turso/libsql support - Base and blog templates β full auth/storage/email/cache setup plus a public blog/visual-editing example
- Standalone build β
pnpm buildworks without any.env(server modules guarded withbuildingflag);Dockerfile+Procfileship in the template for Docker / buildpack deploys - One-line Vite config β
aphex()plugin bundles HMR + dayjs alias + SSR/optimizeDeps tuning so consumers don't copy boilerplate - Fast schema HMR β schema edits hot-swap the engine config without restarting the Vite dev server (~10Γ faster than restart-on-change)
- Documentation site β docs.getaphex.com with LLM-friendly
llms.txt, per-page markdown, and "Copy / Open in ChatGPT / Open in Claude" actions
- Public docs cleanup β keep README, docs site, templates, and package exports aligned with shipped capabilities
- Polish admin UI β improve empty states, settings flows, onboarding, global navigation, and rough edges
- Audit log β append-only actor/action/resource log with admin UI for compliance and client handoff
- Command palette β global
Cmd+Kfor document search, creation, settings, media, org switching, and common actions - Plugin/module API β Sanity-style build-time plugin registration with a declarative permission manifest
- Image transforms β on-the-fly resize / format / crop (see
TODO-image-transforms.md) - Contributor docs expansion β adapter authoring guides, field type authoring guide, plugin/module authoring guide
- Template library β more starters beyond
baseandblog, including premium vertical templates - Migration tools β import/export utilities for content portability between instances
- Webhook system β event-driven integrations on publish / unpublish / delete
- Scheduled publishing β publish-at / unpublish-at timestamps
- Media library enhancements β folders, tags, bulk actions
- Redis-backed cache adapter β drop-in replacement for
InMemoryCacheAdapter - Advanced field types β code editor, color picker, geopoint
- Schema-plane MCP tools β validated schema creation/update helpers that write schema files and run type generation in dev
- Business modules β first-party CRM, customers, people/staff, products/services, bookings/calendar, payments, reviews, and forms
- Automation engine β event triggers, conditions, actions, retries, logs, and workflow history
- Capability toggles β enable native modules without plugin installation or compatibility drift
- Localization (i18n) support β multi-language content with field-level or document-level translation
- Real-time collaboration β multiplayer editing with presence awareness
- Approval workflows β review β approve β publish with role gating
- Monitoring & observability β built-in analytics, slow-query tracking, audit log UI
- MySQL adapter β additional first-party
DatabaseAdapterimplementation
Inspired by Sanity.io β’ Built with SvelteKit, Drizzle ORM, Better Auth, and shadcn-svelte

