Konfigurationsreferenz

Auf dieser Seite

Die Hauptkonfiguration von EmDash befindet sich in astro.config.mjs, während src/live.config.ts den Content-Loader registriert. Deployment-spezifische Werte können auch aus Umgebungsvariablen stammen. Ein kleiner package.json-Metadatenblock unterstützt Template-Bezeichnungen und ältere lokale CLI-Abläufe.

Astro-Integration

Konfiguriere EmDash in astro.config.mjs als Astro-Integration:

import { defineConfig } from "astro/config";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite, libsql } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
			plugins: [],
		}),
	],
});

Integrationsoptionen

database

Erforderlich. Konfiguration des Datenbank-Adapters. Wähle einen Adapter:

// 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" });

Details findest du unter Datenbankoptionen.

migrations

Optional. Steuert, wie die internen Datenbankmigrationen von EmDash zur Laufzeit behandelt werden. Wenn die Option weggelassen wird, gilt standardmäßig { runtime: "auto" }.

migrations: {
	runtime: "check", // "auto" | "check" | "manual"
	dev: "auto",     // optional development override
}

auto prüft ausstehende Migrationen und wendet sie an, check gibt 503 zurück, wenn dem laufenden Build bekannte Migrationen ausstehen (außer bei Anfragen an den Bild-Endpunkt, den EmDash installiert), und manual führt zur Laufzeit keine Migrationsabfrage aus. EMDASH_MIGRATIONS_MODE überschreibt den wirksamen Laufzeitmodus. Lies Core-Datenbankmigrationen verwalten, bevor du check oder manual einführst.

storage

Optional. Konfiguration des Medienspeicher-Adapters. Wenn diese Option fehlt, speichert EmDash Dateien in ./.emdash/uploads und liefert sie über /_emdash/api/media/file aus. Wähle einen Adapter, wenn das lokale Standardverzeichnis nicht geeignet ist:

// 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) — all fields from S3_* environment variables
storage: s3()

// Or with explicit values
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
});

Details findest du unter Speicheroptionen.

images

Optional. Steuert, ob EmDash gespeicherte Medien in die Bildoptimierung von Astro einbindet. Der Standardwert ist true.

Wenn die Option aktiviert ist, umschließt EmDash den Bild-Endpunkt von Astro, sodass <Image> und getImage() die Quellbytes direkt aus dem konfigurierten Speicher-Adapter lesen können. Das funktioniert auch, wenn die ursprüngliche Medien-URL hinter Cloudflare Access liegt. Anfragen an den umschlossenen Endpunkt überspringen den Laufzeitstart von EmDash; daher enthält locals.emdash dort nur storage sowie db für einen angemeldeten Benutzer. locals.user wird wie bei anderen öffentlichen Routen gesetzt. Setze images: false, wenn ein anderer Bilddienst die Medien übernimmt oder wenn jedes Bild ohne den Endpunkt-Wrapper von EmDash gerendert werden soll.

emdash({
	images: false,
});

mediaProviders

Optional. Fügt der Medienbibliothek weitere Mediendienste hinzu. Der speichergestützte lokale Provider bleibt automatisch verfügbar; jeder Descriptor in diesem Array fügt einen weiteren Ort hinzu, an dem Redakteure Medien durchsuchen oder hochladen können.

Das folgende Beispiel fügt Cloudflare Images und Cloudflare Stream hinzu:

import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";

emdash({
	mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});

Die Zugangsdaten der Provider werden zur Laufzeit aufgelöst. Die leeren Konfigurationen oben verwenden die Standard-Umgebungsvariablen von Cloudflare, die in den Adapter-Abschnitten cloudflareImages(config) und cloudflareStream(config) beschrieben sind. Die Einrichtung von Bindings und Rendering findest du unter Medienbibliothek: Medien-Provider.

objectCache

Optional. Speichert Ergebnisse von Inhalts- und Konfigurationsabfragen in einem Key-Value-Speicher, sodass Lesezugriffe ohne Datenbankabfrage bei jeder Anfrage bedient werden. Ohne Angabe deaktiviert. Wähle einen Adapter:

// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });

// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();

Einrichtung und Optionen findest du unter Objekt-Cache.

middleware.outer

Optional. Registriert ein Astro-Middleware-Modul außerhalb des gesamten EmDash-Middleware-Stacks. Weil die Integration es mit Astro order: "pre" registriert, läuft es auch vor der Middleware, die in src/middleware.ts definiert ist. Verwende es für Request-Gates oder Full-Response-Caches, die bei einem Treffer die Initialisierung von Laufzeit und Datenbank vermeiden müssen, oder für Response-Header, die vom finalen HTML von EmDash abhängen.

emdash({
	middleware: {
		outer: "./src/outer-middleware.ts",
	},
});

Die Ausführungsreihenfolge ist:

  1. Die äußere Middleware läuft bis await next().
  2. EmDash initialisiert seine Laufzeit und Datenbank und führt dann die Middleware für Einrichtung, Authentifizierung und Request-Kontext aus.
  3. Die Astro-Route wird gerendert.
  4. EmDash wendet Response-Änderungen an, darunter HTML für die visuelle Bearbeitung sowie Sicherheits- und Timing-Header.
  5. next() wird mit dieser finalen Response an die äußere Middleware aufgelöst.

Vor dem Aufruf von next() hat die Middleware den normalen Astro-Request und den Ausführungskontext der Plattform, aber locals.emdash, locals.user, die Datenbank und der Request-bezogene EmDash-Zustand sind nicht verfügbar. Eine frühe Response überspringt EmDash vollständig und muss daher alle benötigten Sicherheits- und Cache-Header selbst enthalten. Nachdem next() aufgelöst wurde, kannst du gefahrlos CSP-Nonces finalisieren, den vollständigen Body cachen oder Content-Length setzen. Wenn die Middleware den Body ändert, entferne oder berechne einen vorhandenen Content-Length-Header neu.

Der Hook verwendet die Middleware-API von Astro sowohl unter Node als auch unter Cloudflare. Dieses minimale Beispiel mit der Cloudflare Cache API cacht nur anonyme HTML-Responses und liefert Treffer vor der Initialisierung von EmDash zurück:

import { waitUntil } from "cloudflare:workers";
import { defineMiddleware } from "astro:middleware";

export const onRequest = defineMiddleware(async ({ request }, next) => {
	if (request.method !== "GET" || request.headers.has("cookie")) {
		return next();
	}

	const cacheKey = new Request(request.url, { method: "GET" });
	const cached = await caches.default.match(cacheKey);
	if (cached) return cached;

	const response = await next();
	const isHtml = response.headers.get("content-type")?.includes("text/html");
	const isPrivate = response.headers.get("cache-control")?.includes("no-store");
	if (response.ok && isHtml && !isPrivate) {
		waitUntil(caches.default.put(cacheKey, response.clone()));
	}

	return response;
});

Unter Node verwendest du dieselbe Middleware-Form mit einem Node-kompatiblen Cache wie Redis. Cache-Schlüssel und Umgehungsregeln müssen jede Eigenschaft der Anfrage berücksichtigen, die die gerenderte Response verändert.

playground

Optional. Aktiviert die Middleware, die von Wegwerf-Playgrounds für EmDash im Browser verwendet wird. Sie erstellt für jede Sitzung eine beschreibbare Durable-Object-Datenbank, wendet den konfigurierten Seed an und meldet den Besucher als anonymen Administrator an, bevor die normale EmDash-Middleware läuft.

import { playgroundDatabase } from "@emdash-cms/cloudflare";

emdash({
	database: playgroundDatabase({ binding: "PLAYGROUND_DB" }),
	playground: {
		middlewareEntrypoint: "@emdash-cms/cloudflare/db/playground-middleware",
	},
});

Dieser Modus erfordert @emdash-cms/cloudflare und ein Durable-Object-Binding. Er umgeht die normale Middleware für Einrichtung und Authentifizierung; verwende ihn daher nur für kurzlebige Demo-Websites und nicht für ein Produktions-CMS.

plugins

Optional. Array von Plugins, die im selben Prozess wie die Astro-Website laufen. Native Plugins gehören hierher. Ein sandbox-kompatibles Plugin kann hier ebenfalls laufen, wenn du ihm vollen Prozesszugriff anvertraust und keine Isolation brauchst.

Das folgende Beispiel registriert ein natives Plugin:

import seoPlugin from "@emdash-cms/plugin-seo";

plugins: [seoPlugin()];

Native Plugins können Server- und Framework-APIs direkt nutzen und lassen sich daher nicht nach sandboxed verschieben, sofern das Paket nicht zusätzlich einen sandbox-kompatiblen Plugin-Einstiegspunkt bereitstellt. Die Unterschiede bei Entwicklung und Deployment findest du unter Plugin-Format wählen.

sandboxed

Optional. Array sandbox-kompatibler Plugins, die die deklarierten Plugin-APIs von EmDash verwenden und in isolierten Laufzeiten laufen. Platziere hier kein natives Plugin: Nativer Code kann von Prozess- und Framework-Zugriff abhängen, den die Sandbox nicht bietet.

import thirdPartyPlugin from "third-party-emdash-plugin";
import { sandbox } from "@emdash-cms/cloudflare";

emdash({
	sandboxed: [thirdPartyPlugin()],
	sandboxRunner: sandbox(),
});

Sandboxed-Plugins werden übersprungen, wenn kein nutzbarer Sandbox-Runner konfiguriert ist. Die Einrichtung des Runners für Cloudflare und Node.js findest du unter Plugin-Sandbox.

sandboxRunner

Optional. Modulspezifizierer der Factory, die isolierte Plugin-Laufzeiten startet. Sie ist erforderlich für Plugins in sandboxed sowie für Marketplace- oder Registry-Plugins.

Verwende auf Cloudflare Workers den Adapter sandbox():

import { sandbox } from "@emdash-cms/cloudflare";

emdash({
	sandboxRunner: sandbox(),
});

Node.js-Deployments verwenden das workerd-Runner-Modul, das unter Plugin-Sandbox: Node.js dokumentiert ist.

sandbox

Optional. Steuert, ob ein konfigurierter Sandbox-Runner Plugins isoliert. Die Sandbox ist aktiviert, wenn sandboxRunner konfiguriert ist. Setze sandbox: false nur, um zu diagnostizieren, ob ein Problem von einem Plugin oder von dessen Sandbox-Laufzeit stammt:

emdash({
	sandboxRunner: sandbox(),
	sandbox: false,
});

Mit false laufen Plugins, die in sandboxed deklariert oder aus dem Marketplace installiert wurden, im Hauptserverprozess ohne Isolation und ohne Ressourcenlimits. Stelle die Sandbox nach der Diagnose wieder her.

registry

Optional. Konfiguriert den Aggregator und die Richtlinie der Plugin-Registry. Ohne expliziten Wert verwendet EmDash https://registry.emdashcms.com, wenn sandboxRunner konfiguriert und sandbox nicht false ist.

Setze registry: false, um die Registry-Suche und über die Registry installierte Plugins zu deaktivieren, während der Sandbox-Runner für in sandboxed deklarierte Plugins und ältere Marketplace-Plugins verfügbar bleibt:

emdash({
	sandboxRunner: sandbox(),
	registry: false,
});

Übergib die URL des Registry-Dienstes als String oder verwende ein Objekt, wenn die Website Moderationsquellen oder eine Richtlinie zum Release-Alter braucht. Das folgende Beispiel verwendet die Objektform:

import { sandbox } from "@emdash-cms/cloudflare";

emdash({
	sandboxRunner: sandbox(),
	registry: {
		aggregatorUrl: "https://registry.emdashcms.com",
		acceptLabelers: "did:web:labels.emdashcms.com",
		policy: {
			minimumReleaseAge: "48h",
			minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
		},
	},
});
OptionTypBeschreibung
aggregatorUrlstringBasis-URL des Registry-Dienstes. Verwende in Produktion HTTPS.
acceptLabelersstringOptionale, durch Kommas getrennte dezentrale Bezeichner (DIDs) von Moderationsdiensten, die die Anfrage akzeptiert. Eine DID ist ein stabiler Konto-Bezeichner der Atmosphere. Diese Einstellung kann die Richtlinie des Registry-Dienstes nicht überschreiben.
policy.minimumReleaseAgestring | numberHält Releases zurück, die jünger als dieses Alter sind. Dauer-String ("48h", "7d") oder Sekunden.
policy.minimumReleaseAgeExcludestring[]DIDs von Publishern oder Paare <did>/<plugin-slug>, die von der Zurückhaltung ausgenommen sind.

Die Richtlinie zum Release-Alter nimmt das erste Release eines Pakets nur dann aus, wenn die Registry ein aufbewahrtes Release meldet und bestätigt, dass sie das Paket lückenlos beobachtet hat. Ein nachträglich befülltes Paket, ein gelöschtes früheres Release oder fehlende Belege zur Historie lassen die Zurückhaltung in Kraft. Ausdrückliche Ausnahmen für Publisher und Pakete gelten unabhängig von der Historie.

Den Installationsablauf und das Vertrauensmodell findest du unter Die Plugin-Registry.

marketplace

Veraltet. Basis-URL, die zum Aktualisieren von Plugins verwendet wird, die aus dem älteren Marketplace installiert wurden. Das Durchsuchen des Marketplace und neue Installationen werden im Admin nicht angezeigt. Vorhandene Marketplace-Plugins bleiben aktualisierbar und deinstallierbar, solange diese Option konfiguriert ist.

emdash({
	marketplace: "https://marketplace.emdashcms.com",
	sandboxRunner: sandbox(),
});

Produktions-URLs müssen HTTPS verwenden; HTTP wird nur für localhost und 127.0.0.1 während der Entwicklung akzeptiert. Behalte diese Option bei, bis jedes Marketplace-Plugin ersetzt oder deinstalliert wurde, und entferne sie dann. Das vollständige Vorgehen findest du unter Vom Marketplace migrieren.

fonts

Optional. Schriftkonfiguration der Admin-Oberfläche.

Standardmäßig lädt EmDash Noto Sans über die Astro Font API. Die Schriften werden zur Build-Zeit von Google heruntergeladen und selbst gehostet, sodass es zur Laufzeit keine CDN-Anfragen gibt. Die Basisschrift deckt die Schriftsysteme Lateinisch, Kyrillisch, Griechisch, Devanagari und Vietnamesisch ab.

Um weitere Schriftsysteme zu unterstützen, übergib Skriptnamen. Das folgende Beispiel fügt Arabisch und Japanisch hinzu:

emdash({
  fonts: {
    scripts: ["arabic", "japanese"],
  },
})

Die verfügbaren Skripte sind arabic, armenian, bengali, chinese-simplified, chinese-traditional, chinese-hongkong, devanagari, ethiopic, farsi, georgian, gujarati, gurmukhi, hebrew, japanese, kannada, khmer, korean, lao, malayalam, myanmar, oriya, sinhala, tamil, telugu, thai und tibetan.

Jedes Skript entspricht der passenden Noto-Sans-Variante auf Google Fonts (z. B. lädt "arabic" Noto Sans Arabic). Alle Schriftschnitte teilen sich einen einzigen font-family-Namen und verwenden unicode-range, sodass der Browser nur die Dateien herunterlädt, die er für die Zeichen auf der Seite braucht.

Setze die Option auf false, um die Schrifteinbindung vollständig zu deaktivieren und Systemschriften zu verwenden:

emdash({
	fonts: false,
})

Das Admin-CSS verwendet die CSS-Variable --font-emdash. Sie wird von der obigen Schriftkonfiguration automatisch gesetzt.

auth

Optional. Ein Authentifizierungs-Adapter. Die integrierte Anmeldung von EmDash erfolgt über Passkeys; wenn du auth setzt, werden sie durch einen externen Provider ersetzt. Der Cloudflare-Access-Adapter access() wird von @emdash-cms/cloudflare bereitgestellt:

import { access } from "@emdash-cms/cloudflare";

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audience: "your-app-audience-tag",
		roleMapping: {
			Admins: 50,
			Editors: 40,
		},
	}),
});

Optionen für access():

OptionTypStandardBeschreibung
teamDomainstringerforderlichDeine Cloudflare-Access-Team-Domain
audiencestring—Application-Audience-Tag (AUD). Bevorzuge auf Workers audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Umgebungsvariable, aus der der Audience-Tag zur Laufzeit gelesen wird
autoProvisionbooleantrueBeim ersten Login einen EmDash-Benutzer anlegen
defaultRolenumber30Rollenstufe für Benutzer, die nicht von roleMapping erfasst werden (siehe Benutzerrollen)
syncRolesbooleanfalseroleMapping bei jedem Login erneut anwenden, statt nur bei der Bereitstellung
syncNamebooleantrueDen Namen des Benutzers bei jeder authentifizierten Anfrage durch den IdP-Namen ersetzen. Auf false setzen, damit Namen im Admin bearbeitbar sind
roleMappingobject—Ordnet Gruppennamen des Identity Providers EmDash-Rollenstufen zu; der erste Treffer gewinnt

authProviders

Optional. Ein Array austauschbarer Login-Provider (auf oberster Ebene, neben auth). Jeder Eintrag ist das Ergebnis des Aufrufs einer Provider-Factory, wie unten gezeigt:

import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";

emdash({
	authProviders: [github(), google(), atproto()],
});

Integrierte Provider:

  • github() — liest EMDASH_OAUTH_GITHUB_CLIENT_ID / EMDASH_OAUTH_GITHUB_CLIENT_SECRET (oder Fallbacks ohne Präfix).
  • google() — liest EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • microsoft() — liest EMDASH_OAUTH_MICROSOFT_CLIENT_ID / EMDASH_OAUTH_MICROSOFT_CLIENT_SECRET / EMDASH_OAUTH_MICROSOFT_TENANT_ID (oder Fallbacks ohne Präfix). Akzeptiert { emailVerified }. Siehe Microsoft.
  • atproto() — Anmeldung mit einem Atmosphere-Konto (Bluesky und das gesamte AT-Protocol-Netzwerk). Keine Umgebungsvariablen nötig. Akzeptiert { allowedDIDs, allowedHandles, defaultRole }. Siehe den Leitfaden zur Atmosphere-Anmeldung.

Pakete von Drittanbietern können eigene Provider mit derselben Form AuthProviderDescriptor registrieren — siehe Login-Provider.

mcp

Optional. Aktiviert den Endpunkt des Model Context Protocol (MCP) unter /_emdash/api/mcp. Der Endpunkt ist standardmäßig aktiviert und erfordert ein Bearer-Token; seine Aktivierung gewährt also keinen anonymen Zugriff.

Setze die Option auf false, wenn die Website keinen MCP-Endpunkt bereitstellen darf:

emdash({
	mcp: false,
});

Die Erstellung von Tokens und die Konfiguration von Clients findest du in der MCP-Server-Referenz.

siteUrl

Der öffentliche, für den Browser sichtbare Origin der Website (Schema + Host + optionaler Port, kein Pfad). Setze ihn, bevor du die Produktionseinrichtung ausführst, es sei denn, die Website wird auf Cloudflare Workers bereitgestellt. Nur Loopback-Entwicklungshosts können die Einrichtung andernfalls ohne konfigurierten Origin abschließen.

Hinter einem TLS-terminierenden Reverse-Proxy gibt Astro.url die interne Adresse (http://localhost:4321) statt der öffentlichen (https://cms.example.com) zurück. Das stört Passkeys, den Origin-Abgleich für CSRF, OAuth-Weiterleitungen, Login-Weiterleitungen, die MCP-Discovery, Snapshot-Exporte, die Sitemap, robots.txt und strukturierte JSON-LD-Daten. Mit siteUrl behebst du all das auf einmal.

Die Integration validiert diesen Wert beim Laden: Er muss eine gültige URL mit dem Protokoll http: oder https: sein und wird auf den Origin normalisiert (der Pfad wird entfernt).

Das folgende Beispiel setzt den öffentlichen Origin:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	siteUrl: "https://cms.example.com",
});

Wenn siteUrl nicht in der Konfiguration gesetzt ist, prüft EmDash die Umgebungsvariablen in dieser Reihenfolge: EMDASH_SITE_URL, dann SITE_URL. Das ist nützlich für Container-Deployments, bei denen die öffentliche URL zur Laufzeit gesetzt wird.

Die Bildoptimierung liest diese Umgebungsvariablen zur Build-Zeit, weil Astro die erlaubten Bild-Origins in image.remotePatterns festhält, wenn astro build läuft. Ist EMDASH_SITE_URL oder SITE_URL nur in der Laufzeitumgebung gesetzt, etwa bei Hosts, die Variablen erst beim Start der App einspeisen, werden lokal gespeicherte Medien nicht optimiert ausgeliefert. Setzen Sie die Variable auch in der Build-Umgebung oder legen Sie siteUrl in der Konfiguration fest.

Die Einrichtung scheitert auf einem Nicht-Loopback-Host mit SITE_URL_REQUIRED, wenn keine der beiden Quellen gesetzt ist. Das verhindert, dass die erste nicht authentifizierte Einrichtungsanfrage den Origin wählt, der später in Authentifizierungs-E-Mails verwendet wird. Ein Produktions-Build, der auf Cloudflare Workers bereitgestellt wird, ist die Ausnahme: Cloudflare leitet nur Anfragen für Hostnamen weiter, die an den Worker gebunden sind, daher speichert die Einrichtung https:// und den Hostnamen, unter dem sie läuft. Derselbe Build, der über wrangler dev, astro preview oder ein selbst gehostetes workerd auf einer erreichbaren Adresse bereitgestellt wird, bietet diese Garantie nicht; setze dort zuerst siteUrl oder EMDASH_SITE_URL.

Links in E-Mails zur Anmeldung, Einladung, Selbstregistrierung, Wiederherstellung und Kommentarbenachrichtigung verwenden den ersten verfügbaren Origin aus: siteUrl oder deren Umgebungsvariablen, der Site URL unter Settings > General, danach dem bei der Einrichtung festgehaltenen Origin. Verwendet wird nur der Origin der Site URL, und er muss https:// nutzen, sofern der Host nicht localhost, 127.0.0.1 oder [::1] ist. ctx.site.url verwendet denselben Origin wie Links in E-Mails und wird beim Start der Laufzeit gelesen. Wenn Sie eine Site ohne konfiguriertes siteUrl auf eine neue Domain verschoben haben, ändern Sie die Site URL mit Change domain, damit E-Mail-Links darauf zeigen; siehe Zu einer neuen Domain wechseln. Sitemaps und robots.txt verwenden eine andere Reihenfolge, bei der die Site URL vor siteUrl kommt; siehe robots.txt.

Auf Cloudflare Workers liest der Umgebungsvariablen-Fallback process.env. Mit nodejs_compat befüllt Cloudflare process.env für Kompatibilitätsdaten ab 2025-04-01 standardmäßig. Projekte, die auf ein früheres Datum festgelegt sind, müssen zusätzlich nodejs_compat_populate_process_env hinzufügen.

// wrangler.jsonc
{
	"compatibility_date": "2026-02-24",
	"compatibility_flags": ["nodejs_compat"],
	"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}

allowedOrigins

Optional. Zusätzliche Browser-Origins, die die Passkey-Verifizierung für ein Deployment akzeptiert, das unter mehr als einem Hostnamen erreichbar ist.

siteUrl definiert einen einzigen kanonischen Origin. Wenn dasselbe EmDash-Deployment unter mehreren Hostnamen erreichbar ist, die eine registrierbare übergeordnete Domain teilen (z. B. https://example.com und https://preview.example.com), lehnt die Passkey-Verifizierung Assertions ab, deren Origin nicht exakt mit siteUrl übereinstimmt — obwohl WebAuthn erlaubt, dass Passkeys über Subdomains unter derselben rpId hinweg gültig sind.

Deklariere zusätzliche akzeptierte Origins entweder über allowedOrigins in astro.config.mjs oder über die Umgebungsvariable EMDASH_ALLOWED_ORIGINS. Die kanonische siteUrl bleibt die Quelle der rpId; die hier aufgeführten Einträge werden bei der Verifizierung akzeptiert. Beide Quellen werden zur Laufzeit zusammengeführt, sodass die Konfiguration die stabilen Origins (versioniert, im Code-Review geprüft) deklarieren kann, während die Umgebung umgebungsspezifische Extras ergänzt (z. B. kurzlebige PR-Vorschauen).

Das folgende Beispiel deklariert in der Konfiguration einen zusätzlichen Origin:

emdash({
	siteUrl: "https://example.com",
	allowedOrigins: ["https://preview.example.com"],
})

Die entsprechenden Werte können auch aus Umgebungsvariablen stammen:

EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validierung

EmDash validiert diese Werte, um tote Konfiguration zu vermeiden, die der Browser nie berücksichtigen würde:

  • Jeder Eintrag muss eine parsbare http:- oder https:-URL ohne abschließenden Punkt und ohne leere Labels im Hostnamen sein.
  • Wenn allowedOrigins nicht leer ist, muss siteUrl gesetzt sein (über eine der beiden Quellen) und darf weder ein IP-Literal sein noch einen Hostnamen mit abschließendem Punkt haben.
  • Jeder Origin muss derselbe Hostname wie siteUrl oder eine Subdomain davon sein. (WebAuthn verlangt, dass rpId ein registrierbares Suffix jedes Origins ist.)

Wenn die Validierung fehlschlägt, siehst du einen Fehler mit Quellenangabe wie EmDash config error in EMDASH_ALLOWED_ORIGINS: "https://other-site.com" is not a subdomain of siteUrl "https://example.com". Allowed origins must be the same hostname as siteUrl or a subdomain of it.

Wo der Fehler auftritt, hängt davon ab, wo die Werte deklariert sind:

  • Beim Start von Astro, wenn config.allowedOrigins und config.siteUrl beide aus astro.config.mjs stammen — Tippfehler im Code lassen den Build scheitern.
  • Bei der ersten Passkey-Verifizierung, wenn einer der Werte aus EMDASH_ALLOWED_ORIGINS oder EMDASH_SITE_URL stammt — Abweichungen in der Umgebung zeigen sich als 500er beim ersten Verifizierungsversuch.

Reverse-Proxy-Einrichtung

Astro übernimmt X-Forwarded-* nur, wenn der öffentliche Host erlaubt ist. Konfiguriere security.allowedDomains für den Hostnamen (und die Schemata), die deine Benutzer aufrufen. Füge in astro dev passende Einträge in vite.server.allowedHosts hinzu, damit Vite den Host-Header des Proxys akzeptiert.

Korrigiere zuerst allowedDomains (und die Forwarded-Header); verwende siteUrl, wenn die rekonstruierte URL dennoch vom Browser-Origin abweicht (typisch, wenn TLS davor terminiert wird und die Upstream-Anfrage http:// bleibt).

Wenn TLS davor liegt, genügt es oft, den Dev-Server an Loopback zu binden (astro dev --host 127.0.0.1): Der Proxy verbindet sich lokal, während siteUrl dem öffentlichen HTTPS-Origin entspricht.

Wenn dein Proxy einen Client-IP-Header schreibt, setze trustedProxyHeaders, damit die Rate Limits von EmDash die echte Client-IP verwenden können. Ohne vertrauenswürdige Client-IP werden die Rate Limits der Authentifizierung übersprungen, und alle öffentlichen Kommentareinreichungen teilen sich einen Rate-Limit-Bucket (20 Kommentare pro 10 Minuten für alle Besucher zusammen).

Die folgende Konfiguration setzt allowedDomains, vite.server.allowedHosts und siteUrl gemeinsam für ein Deployment hinter einem Reverse-Proxy:

import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";

export default defineConfig({
	security: {
		allowedDomains: [
			{ hostname: "cms.example.com", protocol: "https" },
			{ hostname: "cms.example.com", protocol: "http" },
		],
	},
	vite: {
		server: {
			allowedHosts: ["cms.example.com"],
		},
	},
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
			siteUrl: "https://cms.example.com",
		}),
	],
});

trustedProxyHeaders

Optional. Header, denen für die Ermittlung der Client-IP vertraut wird, wenn die Website hinter einem Reverse-Proxy läuft, den du kontrollierst. Wird von den Rate Limits der Authentifizierung (Magic-Link, Registrierung, Passkey, OAuth Device Flow, OAuth-Client-Registrierung) und vom öffentlichen Kommentar-Endpunkt verwendet.

Auf Cloudflare wird das an die Anfrage angehängte cf-Objekt automatisch verwendet — du musst dies normalerweise nicht setzen. Bei selbst gehosteten Deployments hinter nginx, Caddy, Traefik, Fly, Railway oder Ähnlichem setzt du hier den Header, den dein Proxy schreibt, damit die Rate Limits Anfragen pro echter Client-IP zählen können.

Das folgende Beispiel vertraut dem Header x-real-ip, den nginx, Caddy oder Traefik setzen:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	trustedProxyHeaders: ["x-real-ip"],
});

Die Header werden der Reihe nach probiert. Werte, die auf *-forwarded-for passen, werden als durch Kommas getrennte Listen geparst, und der erste Eintrag wird verwendet. Das folgende Beispiel bevorzugt den Header von Fly.io und fällt auf x-forwarded-for zurück:

emdash({
	trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});

Wenn die Option nicht in der Konfiguration gesetzt ist, liest EmDash die Umgebungsvariable EMDASH_TRUSTED_PROXY_HEADERS (durch Kommas getrennt). Ein explizit leeres Array in der Konfiguration überschreibt die Umgebungsvariable.

maxUploadSize

Optional. Maximal erlaubte Größe für den Upload von Mediendateien in Bytes. Gilt sowohl für direkte Multipart-Uploads als auch für Uploads über signierte URLs. Standardwert ist 52_428_800 (50 MB). Das folgende Beispiel erhöht das Limit auf 100 MB:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
WertBeschreibung
number (Bytes)Muss eine positive endliche Ganzzahl sein
weggelassenStandardmäßig 50 MB

Uploads, die das konfigurierte Limit überschreiten, werden auf dem Pfad für direkte Uploads mit einer Response 413 Payload Too Large abgelehnt, auf dem Pfad mit signierter URL mit 400 Validation Error.

admin

Optional. Ersetzt das EmDash-Branding in der Admin-Oberfläche und begrenzt, welche Admin-Sprachen der Build enthält. Die Branding-Werte ändern weder Titel noch Logo oder Favicon der öffentlichen Website.

emdash({
	admin: {
		logo: "/images/agency-logo.webp",
		siteName: "Agency CMS",
		footerLabel: "AgencyX",
		favicon: "/favicon.ico",
	},
});
OptionTypBeschreibung
logostringLogo-URL oder -Pfad für die Anmeldeseite und die Seitenleiste
siteNamestringName, der im Kopf der Seitenleiste und im Browser-Titel angezeigt wird
footerLabelstring | falseBeschriftung neben der Version in der Seitenleiste. Standard ist "EmDash"; false blendet sie aus
faviconstringFavicon-URL oder -Pfad für Admin-Seiten
localesstring[]Einzubeziehende Admin-Sprachen, etwa ["en", "de"]. Kataloge für andere Sprachen werden aus dem Admin-Bundle ausgelassen, und nur die aufgeführten Sprachen erscheinen im Sprachumschalter. Benutzer, deren bevorzugte Sprachen nicht aufgeführt sind, sehen Englisch. en ist als Fallback immer enthalten, auch wenn Sie es weglassen, sodass admin: { locales: ["en-GB"] } sowohl Englisch als auch Britisches Englisch ausliefert. Ein unbekannter Code lässt den Build fehlschlagen, und der Fehler listet die verfügbaren Codes auf.

updateCheck

Optional. Steuert den Hinweis auf Core-Updates im Admin-Dashboard. Standardwert ist true.

Wenn die Option aktiviert ist, prüft EmDash die öffentliche npm-Registry auf veröffentlichte emdash-Releases — höchstens einmal pro Tag, nach der Response zurückgestellt, sodass sie nie eine Anfrage verlangsamt — und zeigt im Admin-Dashboard ein schließbares Banner an, wenn eine neuere Version verfügbar ist. Wenn du das Banner schließt, wird es für diese Version ausgeblendet; es erscheint wieder, sobald eine neuere Version verfügbar wird.

Das Banner nennt das neueste stabile Release, das seit mindestens 24 Stunden öffentlich ist, sodass ein Release einen Tag nach seiner Veröffentlichung angekündigt wird. Um einer Richtlinie zum Release-Alter in deinem Paketmanager zu entsprechen, etwa minimumReleaseAge von pnpm, setze minimumReleaseAge auf einen Dauer-String ("48h", "7d") oder eine Anzahl von Sekunden. Mit "7d" bietet das Banner das neueste Release an, das mindestens eine Woche alt ist:

emdash({
	updateCheck: { minimumReleaseAge: "7d" },
})

Die Prüfung ist ein einfaches GET an https://registry.npmjs.org/emdash und überträgt keine Website-Daten. Deployments ohne ausgehenden Internetzugang zeigen das Banner schlicht nie an. Setze die Option auf false, um die Prüfung vollständig zu deaktivieren:

emdash({
	updateCheck: false,
})

Das Banner ist nur informativ — EmDash zu aktualisieren bedeutet, das Paket emdash in deinem Projekt anzuheben und neu zu deployen, was das Admin nicht auslösen kann (und nicht auslösen sollte).

toolbar

Optional. Steuert, wie die Redaktions-Symbolleiste (die schwebende Pille auf öffentlichen Seiten) ausgeliefert wird. Standardwert ist "server".

WertVerhalten
"server" (Standard)Die Symbolleiste wird serverseitig in jede HTML-Response eingefügt, die für einen authentifizierten Redakteur gerendert wird.
"client"Das öffentliche HTML ist für jeden Besucher identisch. Ein winziges Bootstrap-Skript zeigt in Browsern, die sich im Admin angemeldet haben, eine „Edit“-Pille an; ein Klick darauf verifiziert die Sitzung und lädt die Seite mit dem Query-Parameter _edit neu, die immer frisch (nie gecacht) mit der vollständigen Symbolleiste gerendert wird.
falseDie Symbolleiste und das Bootstrap-Skript werden nie gerendert.
emdash({
	toolbar: "client",
})

Verwende "client", wenn dein öffentliches HTML über einen geteilten Cache ausgeliefert wird (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Bei serverseitiger Einfügung erhält ein Redakteur, der die öffentliche Website durchstöbert, die gecachte anonyme Variante — ohne Symbolleiste —, sobald ein anonymer Besucher den Cache zuerst befüllt hat; die Symbolleiste erscheint und verschwindet also je nach Cache-Zustand. Im Client-Modus wird nichts Sitzungsspezifisches in teilbares HTML eingefügt, sodass der Cache voll wirksam bleibt und die Symbolleiste zuverlässig ist.

Hinweise zum Modus "client":

  • Abgemeldete Besucher, die eine geteilte ?_edit-URL öffnen, werden auf die kanonische URL umgeleitet, sodass der Parameter weder Entwürfe preisgeben noch zusätzliche Cache-Einträge mit Seiteninhalt befüllen kann.
  • Das Signal „angemeldet“ ist ein nicht geheimes localStorage-Flag, das das Admin setzt; die Pille verifiziert die echte Sitzung, bevor sie die Bearbeitungsansicht öffnet.
  • Das Bootstrap ist ein kleines Inline-<script>. Wenn deine Website ein striktes Content-Security-Policy ohne 'unsafe-inline' sendet, füge einen Hash dafür hinzu — dasselbe gilt für die serverseitig eingefügte Symbolleiste.
  • EmDash fügt nichts Sitzungsspezifisches ein — wenn aber deine eigenen Templates anhand von Astro.locals.user verzweigen (z. B. ein „Admin“-Navigationslink für angemeldete Benutzer), steckt diese Varianz weiterhin in deinem HTML und fragmentiert den Cache weiterhin.

In jedem Modus kann die Symbolleiste im Browser über ihre ×-Schaltfläche geschlossen werden (pro Browser, bis ein Redakteur das nächste Mal das Admin öffnet). Vorschau- und Bearbeitungsmodus-Responses werden immer serverseitig mit Cache-Control: private, no-store gerendert.

Datenbank-Adapter

Importiere die Adapter aus emdash/db:

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

sqlite(config)

SQLite-Datenbank mit dem integrierten Datenbanktreiber von Node.js. Das folgende Beispiel verbindet sich mit einer lokalen Datei:

OptionTypBeschreibung
urlstringDateipfad mit Präfix file:
sqlite({ url: "file:./data.db" });

libsql(config)

libSQL-Datenbank. Das folgende Beispiel verbindet sich mit einer entfernten libSQL-Datenbank:

OptionTypBeschreibung
urlstringDatenbank-URL
authTokenstringAuth-Token zur Laufzeit (für lokale Dateien optional)
migrationAuthTokenEnvstringName der Variable für das Migrations-Token (Standard TURSO_AUTH_TOKEN)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

PostgreSQL-Datenbank mit Connection-Pooling.

OptionTypBeschreibung
connectionStringstringPostgreSQL-Verbindungs-URL
hoststringDatenbank-Host
portnumberDatenbank-Port
databasestringDatenbankname
userstringDatenbankbenutzer
passwordstringDatenbankpasswort
sslbooleanSSL aktivieren
pool.minnumberMinimale Pool-Größe (Standard: 0)
pool.maxnumberMaximale Pool-Größe (Standard: 10)
pool.connectionTimeoutMillisnumberMaximale Wartezeit auf eine Verbindung (pg-Standard: 0, kein Timeout)
pool.idleTimeoutMillisnumberLebensdauer eines inaktiven Clients (pg-Standard: 10.000 ms)
migrationConnectionStringEnvstringName der Variable für die Verbindungszeichenfolge der Migration (Standard DATABASE_URL)

Das folgende Beispiel verbindet sich über eine Verbindungszeichenfolge:

postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

Cloudflare-D1-Datenbank. Import aus @emdash-cms/cloudflare.

OptionTypStandardBeschreibung
bindingstring—Name des D1-Bindings aus wrangler.jsonc
sessionstring"disabled"Modus der Lese-Replikation: "disabled", "auto" oder "primary-first"
bookmarkCookiestring"__em_d1_bookmark"Cookie-Name für Sitzungs-Bookmarks
coalescebooleanfalseBündelt gleichzeitige Lesezugriffe im selben Event-Loop-Durchlauf; erfordert einen anderen Sitzungsmodus als "disabled"

Das folgende Beispiel zeigt ein einfaches Binding und eines mit aktivierten Lese-Replikaten:

// Basic
d1({ binding: "DB" });

// With read replicas
d1({ binding: "DB", session: "auto" });

Wenn session auf "auto" oder "primary-first" steht, verwendet EmDash die D1-Sessions-API, um Leseabfragen an nahe gelegene Replikate zu leiten. Authentifizierte Benutzer erhalten Read-your-writes-Konsistenz auf Basis von Bookmarks. Details findest du unter Datenbankoptionen — Lese-Replikate.

hyperdrive(config?)

PostgreSQL über ein Cloudflare-Hyperdrive-Binding. Importiere diesen Adapter aus @emdash-cms/cloudflare.

OptionTypStandardBeschreibung
bindingstring"HYPERDRIVE"Primäres Hyperdrive-Binding mit deaktiviertem Query-Caching
cachedBindingstring—Optionales zweites Binding mit aktiviertem Caching für anonyme öffentliche Lesezugriffe
preferUncachedAfterWriteMsnumber60_000Wie lange öffentliche Lesezugriffe nach einem Inhaltsschreibzugriff das primäre Binding verwenden, wenn cachedBinding gesetzt ist
migrationConnectionStringEnvstringAus dem primären Binding abgeleitetUmgebungsvariable mit der direkten PostgreSQL-URL, die emdash migrate verwendet
maxnumber5Maximale Anzahl an Verbindungen eines Worker-Isolates zu Hyperdrive

Das folgende Beispiel leitet authentifizierte Anfragen und Schreibzugriffe über das ungecachte Binding, während anonyme öffentliche Lesezugriffe das gecachte Binding verwenden können:

hyperdrive({
	binding: "HYPERDRIVE",
	cachedBinding: "HYPERDRIVE_CACHED",
	preferUncachedAfterWriteMs: 60_000,
});

Beide Bindings müssen auf dieselbe Datenbank zeigen. Installiere pg in Version 8.16.3 oder höher, aktiviere das Kompatibilitäts-Flag nodejs_compat und konfiguriere eine direkte Datenbank-URL für Deployment-Migrationen. Die vollständige Einrichtung von Worker und Migration findest du unter Datenbankoptionen: Hyperdrive.

durableObjects(config)

Speichert das CMS in einem einzigen SQLite-gestützten Durable Object. Importiere diesen Adapter aus @emdash-cms/cloudflare.

OptionTypStandardBeschreibung
bindingstringerforderlichDurable-Object-Namespace-Binding für die Klasse EmDashDB
namestring"emdash"Name des Singleton-Objekts; ändere ihn nur, um mehrere Datenbanken hinter einem Binding zu isolieren
sessionstring"disabled""auto" leitet anonyme Lesezugriffe an Replikate und Schreibzugriffe an das primäre Objekt
bookmarkCookiestring"__em_do_bookmark"Cookie für Read-your-writes-Konsistenz im Modus "auto"
durableObjects({ binding: "DB_DO", session: "auto" });

Das Routing zu Replikaten erfordert die Kompatibilitäts-Flags experimental und replica_routing sowie die Einträge für Durable-Object-Klasse und Migration in wrangler.jsonc.

previewDatabase(config)

Erstellt pro Vorschau-Sitzung eine isolierte Snapshot-Datenbank in einem Durable Object. Die einzige Option ist der erforderliche Name binding:

previewDatabase({ binding: "PREVIEW_DB" });

Dieser Adapter ist für die Vorschau-Infrastruktur gedacht, nicht für die primäre Datenbank einer Produktions-Website.

playgroundDatabase(config)

Erstellt pro Playground-Sitzung eine beschreibbare, mit einem Seed befüllte Datenbank in einem Durable Object. Kombiniere ihn mit der Integrationsoption playground:

playgroundDatabase({ binding: "PLAYGROUND_DB" });

Das erforderliche binding bezeichnet den Durable-Object-Namespace des Playgrounds. Verwende diesen Adapter nur für Wegwerf-Demo-Websites.

Speicher-Adapter

Importiere local und s3 aus emdash/astro. Der Adapter r2 wird aus @emdash-cms/cloudflare importiert:

import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

local(config)

Speicherung im lokalen Dateisystem. Das folgende Beispiel liefert Uploads aus einem lokalen Verzeichnis aus:

OptionTypBeschreibung
directorystringVerzeichnispfad
baseUrlstringBasis-URL zum Ausliefern von Dateien
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Cloudflare-R2-Binding. Das folgende Beispiel verwendet ein R2-Binding mit einer öffentlichen URL:

OptionTypBeschreibung
bindingstringName des R2-Bindings
publicUrlstringOptionale öffentliche URL
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

S3-kompatibler Speicher. Alle Konfigurationsfelder sind optional: Jedes Feld, das in s3({...}) fehlt, wird beim Start des Node-Prozesses aus der passenden Umgebungsvariable S3_* aufgelöst. Explizite Werte haben immer Vorrang.

Nachdem Konfigurations- und Umgebungswerte zusammengeführt wurden, sind endpoint und bucket erforderlich. Wenn eine der beiden Zugangsdaten gesetzt ist, sind sowohl accessKeyId als auch secretAccessKey erforderlich. Fehlende Werte lassen den Start mit dem Fehlercode MISSING_S3_CONFIG scheitern.

Voraussetzung: Installiere @aws-sdk/client-s3 und @aws-sdk/s3-request-presigner in deinem Projekt. EmDash Core bündelt das AWS SDK nicht. Details findest du unter Speicheroptionen: S3-kompatibler Speicher.

OptionTypBeschreibung
endpointstringS3-Endpunkt-URL (S3_ENDPOINT)
bucketstringBucket-Name (S3_BUCKET)
accessKeyIdstringZugriffsschlüssel (S3_ACCESS_KEY_ID)
secretAccessKeystringGeheimer Schlüssel (S3_SECRET_ACCESS_KEY)
regionstringRegion, Standard "auto" (S3_REGION)
publicUrlstringOptionale CDN-URL (S3_PUBLIC_URL)

Die folgenden Beispiele lösen alle Felder aus der Umgebung auf, mischen Konfiguration und Umgebung oder übergeben jedes Feld explizit:

// All fields from S3_* environment variables (Node container deployments)
s3()

// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })

// All explicit
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",
})

Die Auflösung von Umgebungsvariablen zur Laufzeit ist ein reines Node-Feature. Auf Cloudflare Workers werden Secrets und Variablen über den Parameter env des Fetch-Handlers bereitgestellt, nicht über process.env; S3_*-Umgebungsvariablen werden daher nicht übernommen. Workers-Deployments sollten entweder den Adapter r2(config) verwenden oder explizite Werte an s3({...}) übergeben. Details findest du unter Speicheroptionen.

Objekt-Cache-Adapter

Übergib einen davon an die Option objectCache.

kvCache(config)

Cloudflare-KV-Backend, über alle Isolates hinweg geteilt. Import aus @emdash-cms/cloudflare.

kvCache({
	binding: "CACHE", // KV binding name (required)
	defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
	revalidate: 1000, // cross-isolate staleness window in ms (optional)
	timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
	keyPrefix: "em", // cache key prefix (optional)
})

memoryCache(config?)

In-Process-Backend für Node.js und die Entwicklung. Import aus emdash/astro.

memoryCache({
	defaultTtl: 3600, // entry TTL in seconds (optional)
	revalidate: 1000, // staleness window in ms (optional)
	maxEntries: 1000, // max cached keys before eviction (optional)
	keyPrefix: "em", // cache key prefix (optional)
})

Einrichtung und Verhalten findest du unter Objekt-Cache.

Authentifizierungs- und Sandbox-Adapter

Diese Adapter liefern Werte für die Integrationsoptionen auth und sandboxRunner.

access(config)

Ersetzt die integrierte Passkey-Anmeldung durch die Authentifizierung über Cloudflare Access. Importiere sie aus @emdash-cms/cloudflare und übergib das Ergebnis an auth:

import { access } from "@emdash-cms/cloudflare";

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audienceEnvVar: "CF_ACCESS_AUDIENCE",
	}),
});

teamDomain ist erforderlich. Der Adapter kann die Application Audience aus audience oder aus der Variable lesen, die audienceEnvVar benennt; er akzeptiert außerdem autoProvision, defaultRole, syncRoles, syncName und roleMapping. Die Option auth dokumentiert deren Standardwerte, das Rollenverhalten und die Namenssynchronisierung.

sandbox()

Wählt den Cloudflare Worker Loader als Plugin-Sandbox-Runner. Importiere ihn aus @emdash-cms/cloudflare und übergib seinen Rückgabewert an sandboxRunner:

import { sandbox } from "@emdash-cms/cloudflare";

emdash({
	sandboxRunner: sandbox(),
});

Die Website braucht außerdem ein Worker-Loader-Binding und den Einstiegspunkt der Plugin-Bridge. Diese Deployment-Einstellungen findest du unter Plugin-Sandbox: Cloudflare Workers.

Medien-Provider-Adapter

Übergib Medien-Provider-Descriptors an mediaProviders. Beide integrierten Cloudflare-Provider werden aus @emdash-cms/cloudflare importiert.

Jede unten genannte *EnvVar-Option benennt eine Umgebungsvariable. Der Provider liest sie in dieser Reihenfolge: ein Cloudflare-Workers-Binding dieses Namens, dann process.env beim Node-Adapter. Die passende direkte Option (accountId, accountHash, apiToken) hat immer Vorrang vor beiden.

cloudflareImages(config)

Fügt Cloudflare Images zum Durchsuchen, Hochladen, Löschen und Ausliefern von Bild-Assets hinzu.

OptionTypStandardBeschreibung
accountIdstringAus CF_ACCOUNT_IDCloudflare-Konto-ID
accountIdEnvVarstring"CF_ACCOUNT_ID"Variable, die verwendet wird, wenn accountId fehlt
accountHashstringAus CF_IMAGES_ACCOUNT_HASHIn Auslieferungs-URLs verwendeter Konto-Hash
accountHashEnvVarstring"CF_IMAGES_ACCOUNT_HASH"Variable, die verwendet wird, wenn accountHash fehlt
apiTokenstringAus CF_IMAGES_TOKENToken mit Lese- und Bearbeitungsrechten für Cloudflare Images
apiTokenEnvVarstring"CF_IMAGES_TOKEN"Variable, die verwendet wird, wenn apiToken fehlt
deliveryDomainstringimagedelivery.netEigener Hostname für die Bildauslieferung
defaultVariantstring"public"Für die Anzeige verwendete Bildvariante
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];

cloudflareStream(config)

Fügt Cloudflare Stream zum Durchsuchen, Suchen, Hochladen, Löschen und Abspielen von Video-Assets hinzu.

OptionTypStandardBeschreibung
accountIdstringAus CF_ACCOUNT_IDCloudflare-Konto-ID
accountIdEnvVarstring"CF_ACCOUNT_ID"Variable, die verwendet wird, wenn accountId fehlt
apiTokenstringAus CF_STREAM_TOKENToken mit Lese- und Bearbeitungsrechten für Cloudflare Stream
apiTokenEnvVarstring"CF_STREAM_TOKEN"Variable, die verwendet wird, wenn apiToken fehlt
customerSubdomainstringCloudflare-StandardEigener Hostname für die Stream-Auslieferung
controlsbooleantruePlayer-Steuerelemente anzeigen
autoplaybooleanfalseWiedergabe automatisch starten
loopbooleanfalseWiedergabe wiederholen
mutedbooleanfalse, oder true mit AutoplayWiedergabe stummschalten
mediaProviders: [cloudflareStream({ controls: true })];

Die erforderlichen Bindings und Rendering-Komponenten findest du unter Medienbibliothek: Medien-Provider.

Live-Collections

Konfiguriere den EmDash-Loader in src/live.config.ts:

import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

export const collections = {
	_emdash: defineLiveCollection({
		loader: emdashLoader(),
	}),
};

Loader-Optionen

Die Funktion emdashLoader() akzeptiert keine Argumente:

emdashLoader();

Umgebungsvariablen

EmDash berücksichtigt diese Umgebungsvariablen:

VariableBeschreibung
EMDASH_SITE_URLÖffentlicher, für den Browser sichtbarer Origin (fällt auf SITE_URL zurück)
EMDASH_ALLOWED_ORIGINSDurch Kommas getrennte Liste zusätzlicher Origins, die die Passkey-Verifizierung akzeptiert (Deployments mit mehreren Subdomains).
EMDASH_DATABASE_URLDatenbank-URL überschreiben
EMDASH_ENCRYPTION_KEYSchlüssel zum Verschlüsseln von Plugin-Secrets im Ruhezustand. Vom Betreiber bereitgestellt — wird nie in der Datenbank gespeichert.
EMDASH_PREVIEW_SECRETOptionale Überschreibung des HMAC-Secrets für die Vorschau. Wenn nicht gesetzt, wird ein stabiler Wert pro Website erzeugt und in der Datenbank gespeichert.
EMDASH_IP_SALTOptionale Überschreibung des Salts für den IP-Hash von Kommentierenden. Wenn nicht gesetzt, wird ein stabiler Wert pro Website erzeugt und in der Datenbank gespeichert.
EMDASH_AUTH_SECRETVeraltet. Wird, falls gesetzt, als Quelle für den IP-Salt verwendet; bestehende Installationen sollten dies beibehalten, um über ein Upgrade hinweg stabile IP-Hashes von Kommentierenden zu bewahren.
EMDASH_TURNSTILE_SECRET_KEYSecret Key von Cloudflare Turnstile (fällt auf TURNSTILE_SECRET_KEY zurück). Wenn gesetzt, müssen Kommentareinreichungen ein gültiges Turnstile-Token enthalten — kombiniere dies mit der Prop turnstileSiteKey an <CommentForm>.
EMDASH_URLURL einer entfernten EmDash-Instanz für die Schema-Synchronisierung

Erzeuge einen Verschlüsselungsschlüssel mit dem folgenden Befehl:

npx emdash secrets generate

package.json-Konfiguration

Templates und Websites können unter dem Schlüssel emdash in der package.json optionale Metadaten deklarieren:

{
	"emdash": {
		"label": "My Blog Template",
		"schema": ".emdash/schema.sql",
		"seed": ".emdash/seed.json"
	}
}
OptionBeschreibung
labelTemplate-Name zur Anzeige
schemaOptionales SQL-Schema, das emdash init liest
seedPfad zur Seed-JSON-Datei

TypeScript-Konfiguration

Während der lokalen Entwicklung erzeugt die Astro-Integration emdash-env.d.ts im Projektstamm und aktualisiert die Datei nach Schemaänderungen. Die Datei erweitert das Modul emdash, sodass die Standard-Imports getEmDashCollection() und getEmDashEntry() lokale Collection-Felder ohne Pfad-Alias ableiten.

Der separate Befehl emdash types ruft das Schema von einer laufenden lokalen oder entfernten Instanz ab und schreibt standardmäßig .emdash/types.ts. Füge nur dann einen Alias hinzu, wenn Anwendungscode diese eigenständige Ausgabe direkt importiert:

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

Erzeuge die eigenständigen Typen des entfernten Schemas mit dem folgenden Befehl:

npx emdash types