Skip to content

Configuration Reference

EmDash is configured through two files: astro.config.mjs for the integration and src/live.config.ts for content collections.

Configure EmDash as an Astro integration:

astro.config.mjs
import { defineConfig } from "astro/config";
import emdash, { local, r2, s3 } from "emdash/astro";
import { sqlite, libsql, d1 } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
plugins: [],
}),
],
});

Required. Database adapter configuration.

// SQLite (Node.js)
database: sqlite({ url: "file:./data.db" });
// PostgreSQL
database: postgres({ connectionString: process.env.DATABASE_URL });
// libSQL
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
// Cloudflare D1 (import from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });

See Database Options for details.

Required. Media storage adapter configuration.

// Local filesystem (development)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 binding (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform)
storage: s3({
endpoint: "https://s3.amazonaws.com",
bucket: "my-bucket",
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: "us-east-1", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});

See Storage Options for details.

Optional. Array of EmDash plugins.

import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];

Optional. Authentication configuration.

auth: {
// Self-signup configuration
selfSignup: {
domains: ["example.com"],
defaultRole: 20, // Contributor
},
// OAuth providers
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
},
// Session configuration
session: {
maxAge: 30 * 24 * 60 * 60, // 30 days
sliding: true, // Reset expiry on activity
},
// OR use Cloudflare Access (exclusive mode)
cloudflareAccess: {
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
autoProvision: true,
defaultRole: 30,
syncRoles: false,
roleMapping: {
"Admins": 50,
"Editors": 40,
},
},
}

Allow users to self-register if their email domain is allowed.

OptionTypeDefaultDescription
domainsstring[][]Allowed email domains
defaultRolenumber20Role for self-signups
selfSignup: {
domains: ["example.com", "acme.org"],
defaultRole: 20, // Contributor
}

Configure OAuth login providers.

oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
}

Session configuration.

OptionTypeDefaultDescription
maxAgenumber2592000 (30d)Session lifetime in seconds
slidingbooleantrueReset expiry on activity

Use Cloudflare Access as the authentication provider instead of passkeys.

OptionTypeDefaultDescription
teamDomainstringrequiredYour Access team domain
audiencestringrequiredApplication Audience (AUD) tag
autoProvisionbooleantrueCreate users on first login
defaultRolenumber30Default role for new users
syncRolesbooleanfalseUpdate role on each login
roleMappingobjectMap IdP groups to roles

Import from emdash/db:

import { sqlite, libsql, postgres, d1 } from "emdash/db";

SQLite database using better-sqlite3.

OptionTypeDescription
urlstringFile path with file: prefix
sqlite({ url: "file:./data.db" });

libSQL database.

OptionTypeDescription
urlstringDatabase URL
authTokenstringAuth token (optional for local files)
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});

PostgreSQL database with connection pooling.

OptionTypeDescription
connectionStringstringPostgreSQL connection URL
hoststringDatabase host
portnumberDatabase port
databasestringDatabase name
userstringDatabase user
passwordstringDatabase password
sslbooleanEnable SSL
pool.minnumberMinimum pool size (default: 0)
pool.maxnumberMaximum pool size (default: 10)
postgres({ connectionString: process.env.DATABASE_URL });

Cloudflare D1 database. Import from @emdash-cms/cloudflare.

OptionTypeDefaultDescription
bindingstringD1 binding name from wrangler.jsonc
sessionstring"disabled"Read replication mode: "disabled", "auto", or "primary-first"
bookmarkCookiestring"__ec_d1_bookmark"Cookie name for session bookmarks
// Basic
d1({ binding: "DB" });
// With read replicas
d1({ binding: "DB", session: "auto" });

When session is "auto" or "primary-first", EmDash uses the D1 Sessions API to route read queries to nearby replicas. Authenticated users get bookmark-based read-your-writes consistency. See Database Options — Read Replicas for details.

Import from emdash/astro:

import emdash, { local, r2, s3 } from "emdash/astro";

Local filesystem storage.

OptionTypeDescription
directorystringDirectory path
baseUrlstringBase URL for serving files
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});

Cloudflare R2 binding.

OptionTypeDescription
bindingstringR2 binding name
publicUrlstringOptional public URL
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});

S3-compatible storage.

OptionTypeDescription
endpointstringS3 endpoint URL
bucketstringBucket name
accessKeyIdstringAccess key
secretAccessKeystringSecret key
regionstringRegion (default: "auto")
publicUrlstringOptional CDN URL
s3({
endpoint: "https://xxx.r2.cloudflarestorage.com",
bucket: "media",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
publicUrl: "https://cdn.example.com",
});

Configure the EmDash loader in src/live.config.ts:

src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};

The emdashLoader() function accepts optional configuration:

emdashLoader({
// Currently no options - reserved for future use
});

EmDash respects these environment variables:

VariableDescription
EMDASH_DATABASE_URLOverride database URL
EMDASH_AUTH_SECRETSecret for passkey authentication
EMDASH_PREVIEW_SECRETSecret for preview token generation
EMDASH_URLRemote EmDash URL for schema sync

Generate an auth secret with:

Terminal window
npx emdash auth secret

Optional configuration in package.json:

package.json
{
"emdash": {
"label": "My Blog Template",
"description": "A clean, minimal blog template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev",
"preview": "https://emdash-blog.pages.dev"
}
}
OptionDescription
labelTemplate name for display
descriptionTemplate description
seedPath to seed JSON file
urlRemote URL for schema sync
previewDemo site URL for template preview

EmDash generates types in .emdash/types.ts. Add to your tsconfig.json:

tsconfig.json
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}

Generate types with:

Terminal window
npx emdash types