# Configuration ## astro.config.mjs ### Node.js (local development / self-hosted) ```javascript import node from "@astrojs/node"; import react from "@astrojs/react"; import { defineConfig } from "astro/config"; import emdash, { local } from "emdash/astro"; import { sqlite } from "emdash/db"; export default defineConfig({ output: "server", adapter: node({ mode: "standalone" }), image: { layout: "constrained", responsiveStyles: true, }, integrations: [ react(), emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file", }), }), ], devToolbar: { enabled: false }, }); ``` ### Reverse proxy and passkeys Passkey `rpId` / `origin` follow Astro `context.url`, which only reflects `X-Forwarded-*` when you declare **allowed public hosts** ([`security.allowedDomains`](https://docs.astro.build/en/reference/configuration-reference/#securityalloweddomains)). In dev, add matching **`vite.server.allowedHosts`** or Vite rejects the proxy `Host`. Use **`emdash({ passkeyPublicOrigin: "https://…" })`** when the browser origin and reconstructed URL still disagree (common with TLS termination). With TLS terminated in front, **`astro dev --host 127.0.0.1`** (loopback) is usually enough: the proxy reaches the dev server locally while **`passkeyPublicOrigin`** matches the browser’s HTTPS origin—without opening the Node port on the LAN. ### Cloudflare (D1 + R2) ```javascript import cloudflare from "@astrojs/cloudflare"; import react from "@astrojs/react"; import { d1, r2 } from "@emdash-cms/cloudflare"; import { defineConfig } from "astro/config"; import emdash from "emdash/astro"; export default defineConfig({ output: "server", adapter: cloudflare(), image: { layout: "constrained", responsiveStyles: true, }, integrations: [ react(), emdash({ database: d1({ binding: "DB", session: "auto" }), storage: r2({ binding: "MEDIA" }), }), ], devToolbar: { enabled: false }, }); ``` Requires a `wrangler.jsonc` with D1 and R2 bindings: ```jsonc { "name": "my-site", "compatibility_date": "2026-02-24", "compatibility_flags": ["nodejs_compat"], "assets": { "directory": "./dist" }, "d1_databases": [ { "binding": "DB", "database_name": "my-site", "database_id": "", // from `wrangler d1 create my-site` }, ], "r2_buckets": [ { "binding": "MEDIA", "bucket_name": "my-site-media", }, ], } ``` ### Plugins Register plugins in `astro.config.mjs`: ```javascript import { auditLogPlugin } from "@emdash-cms/plugin-audit-log"; emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }), plugins: [auditLogPlugin()], }), ``` ## live.config.ts Every EmDash site needs this file at `src/live.config.ts`. It's boilerplate -- the same in every project: ```typescript import { defineLiveCollection } from "astro:content"; import { emdashLoader } from "emdash/runtime"; export const collections = { _emdash: defineLiveCollection({ loader: emdashLoader() }), }; ``` This registers EmDash's live content collections with Astro. All content types are served through the single `_emdash` collection -- you query specific types using `getEmDashCollection("posts")` etc. ## emdash-env.d.ts Auto-generated at the project root when the dev server starts. Provides TypeScript types for your collections. This is the file your `tsconfig.json` includes. ```typescript /// import type { PortableTextBlock } from "emdash"; export interface Post { id: string; slug: string | null; status: string; title: string; featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; }; content?: PortableTextBlock[]; excerpt?: string; createdAt: Date; updatedAt: Date; publishedAt: Date | null; } declare module "emdash" { interface EmDashCollections { posts: Post; } } ``` The dev server regenerates this file automatically when schema changes. You can also generate it manually: ## Type Generation ```bash # From local dev server (writes emdash-env.d.ts at project root) npx emdash types # From remote instance npx emdash types --url https://my-site.pages.dev # Custom output path npx emdash types --output src/types/cms.ts ``` The CLI also writes `.emdash/schema.json` with the raw schema for tooling. ## package.json Key dependencies for a Node.js site: ```json { "dependencies": { "astro": "^6.0.0", "emdash": "workspace:*", "@astrojs/node": "^9.0.0", "@astrojs/react": "^4.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } } ``` For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emdash-cms/cloudflare`. ## Dev Server ```bash npx emdash dev # Start dev server (runs migrations, applies seed) npx emdash dev --types # Start and generate types from schema ``` The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.