Skip to content

Configuration

This page is a reference, not a tutorial. For the path through, start with Quickstart.

astro.config.mjs
import caret from '@caretcms/core';
caret({
// Mode
mode: 'embedded' | 'cloud',
// Routes
mountPath: '/admin',
apiBasePath: '/api/cms',
// Toggles
enableAdmin: true,
enableInlineEditor: true,
// Storage and uploads (embedded mode only)
storage: filesystemStorage(),
uploads: localUploads(),
// Cloud (cloud mode only)
cloud: {
endpoint: 'https://cloud.caretcms.com',
projectId: 'proj_marketing_site',
contentPath: '/content',
publicToken: '...',
environment: 'production',
},
// Schemas
schemas: { /* collection: jsonSchema */ },
})
OptionDefaultType
mode'embedded''embedded' | 'cloud'
mountPath'/admin'string
apiBasePath'/api/cms'string
enableAdmintrue (embedded), false (cloud)boolean
enableInlineEditortrue (embedded), false (cloud)boolean
storagefilesystemStorage()CaretStorageProvider
uploadslocalUploads()CaretUploadProvider
cloudCaretCloudOptions (required when mode: 'cloud')
schemas{}Record<string, JsonSchemaDefinition>
  • Default: 'embedded'
  • Values: 'embedded' | 'cloud' Alpha

embedded runs the CMS inside your Astro site — middleware, admin UI, API routes, all served from the same origin. This is what you want today.

cloud is alpha. The integration accepts the option and ships a client bootstrap, but the hosted control plane isn’t live yet.

Where the admin UI mounts. Login is ${mountPath}, Studio is ${mountPath}/cms.

caret({ mountPath: '/staff' })
// → /staff, /staff/cms

Use this if /admin collides with your app’s existing routes.

Base path for all API routes. Every endpoint hangs off this:

  • ${apiBasePath}/entries
  • ${apiBasePath}/mutate
  • ${apiBasePath}/schema
  • ${apiBasePath}/auth/login
  • …etc

If your project’s src/pages/api/ already has CMS-adjacent routes, change this to avoid collisions.

Whether to inject the admin routes (mountPath, mountPath/cms, mountPath/themes). Set to false to disable Studio entirely while keeping the API surface (e.g. for a pure-headless setup or to disable editing in prod).

Whether to inject the inline-editor bootstrap script onto pages. Disable to turn off data-caret click-to-edit while keeping Studio working. Useful in production if you want editing to happen only via the admin UI, not on live pages.

Where entries, revisions, history, and dynamic-collection metadata live. See Storage Adapters.

import { filesystemStorage } from '@caretcms/core';
filesystemStorage({
dataRoot: 'src/content/cms', // optional — default: '.caret/data'
metaRoot: 'src/content/cms/.meta', // optional — default: '.caretcms'
})

Where uploaded files (images via <img data-caret>) go. Same shape as storage.

import { localUploads } from '@caretcms/core';
localUploads({ uploadsDir: './public/uploads' }) // default: 'public/uploads'

Required when mode: 'cloud'. Ignored otherwise.

FieldDefaultNotes
endpointHosted control plane URL. Required.
projectIdProject ID in the hosted control plane. Required.
contentPath'/content'API base path on the hosted endpoint.
publicTokenundefinedPublic bootstrap token (per-project).
environmentundefinedEnvironment name ('preview', 'production').

Map of collection name → JSON Schema. Highest priority schema source. See Schemas.

caret({
schemas: {
pages: z.toJSONSchema(PageSchema),
site: z.toJSONSchema(SiteSchema),
},
})
VariableRequiredPurpose
CARET_EDIT_PASSWORDYes (for editing)Editor password — anyone with it can log in
EDIT_PASSWORDFallbackSame as above; kept for backward compat
CARET_SESSION_SECRETStrongly recommended in prodHMAC secret for session cookies
CARET_SESSION_TTL_HOURSOptionalOverride the default 12h session TTL

See Authentication & Security for how each one is used.

Cloudflare deployments also need bindings declared in wrangler.toml:

BindingPurpose
CMS_KV (or your custom name)KV namespace for storage
CMS_R2 (or your custom name)R2 bucket for uploads
{
mode: 'embedded',
mountPath: '/admin',
apiBasePath: '/api/cms',
enableAdmin: true,
enableInlineEditor: true,
storage: filesystemStorage(), // → .caret/data/
uploads: localUploads(), // → public/uploads/
schemas: {}, // → all collections inferred
}
caret({
enableAdmin: import.meta.env.PROD === false,
enableInlineEditor: import.meta.env.PROD === false,
})