Référence de configuration

Sur cette page

La configuration principale d’EmDash se trouve dans astro.config.mjs, tandis que src/live.config.ts enregistre le chargeur de contenu. Des valeurs propres au déploiement peuvent aussi provenir de variables d’environnement. Un petit bloc de métadonnées emdash dans package.json prend en charge les libellés de modèle et les flux CLI locaux hérités.

Intégration Astro

Configurez EmDash comme intégration Astro dans astro.config.mjs :

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: [],
		}),
	],
});

Options d’intégration

database

Obligatoire. Configuration de l’adaptateur de base de données. Choisissez un adaptateur :

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

Consultez Options de base de données pour les détails.

migrations

Optionnel. Contrôle le traitement à l’exécution des migrations internes de la base de données EmDash. Si cette option est omise, la valeur par défaut est { runtime: "auto" }.

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

auto vérifie et applique les migrations en attente, check renvoie 503 lorsque des migrations connues du build en cours sont en attente, et manual n’effectue aucune requête de migration à l’exécution. EMDASH_MIGRATIONS_MODE remplace le mode d’exécution effectif. Consultez Gérer les migrations de la base de données du noyau avant d’adopter check ou manual.

storage

Optionnel. Configuration de l’adaptateur de stockage des médias. Lorsque cette option est omise, EmDash stocke les fichiers dans ./.emdash/uploads et les sert via /_emdash/api/media/file. Choisissez un adaptateur lorsque le répertoire local par défaut ne convient pas :

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

Consultez Options de stockage pour les détails.

images

Optionnel. Contrôle si EmDash intègre les médias stockés à l’optimisation d’images d’Astro. La valeur par défaut est true.

Lorsque cette option est activée, EmDash encapsule le point de terminaison d’images d’Astro afin que <Image> et getImage() puissent lire les octets source directement depuis l’adaptateur de stockage configuré. Cela fonctionne aussi lorsque l’URL média d’origine est derrière Cloudflare Access. Définissez images: false lorsqu’un autre service d’images gère les médias ou lorsque chaque image doit être rendue sans l’encapsulation du point de terminaison EmDash.

emdash({
	images: false,
});

mediaProviders

Optionnel. Ajoute des services média à la bibliothèque de médias. Le fournisseur local basé sur le stockage reste disponible automatiquement ; chaque descripteur de ce tableau ajoute un autre endroit où les éditeurs peuvent parcourir ou téléverser des médias.

L’exemple suivant ajoute Cloudflare Images et Cloudflare Stream :

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

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

Les identifiants des fournisseurs sont résolus à l’exécution. Les configurations vides ci-dessus utilisent les variables d’environnement Cloudflare par défaut décrites dans les sections des adaptateurs cloudflareImages(config) et cloudflareStream(config). Consultez Bibliothèque de médias : fournisseurs de médias pour la configuration des liaisons et du rendu.

objectCache

Optionnel. Met en cache les résultats de requêtes de contenu et de configuration dans un magasin clé/valeur afin que les lectures soient servies sans interroger la base de données à chaque requête. Désactivé lorsque l’option est omise. Choisissez un adaptateur :

// 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();

Consultez Cache d’objets pour la configuration et les options.

middleware.outer

Optionnel. Enregistre un module middleware Astro en dehors de la pile middleware EmDash complète. Comme l’intégration l’enregistre auprès d’Astro avec order: "pre", il s’exécute aussi avant le middleware défini dans src/middleware.ts. Utilisez-le pour des portes de requête ou des caches de réponse complète qui doivent éviter l’initialisation du runtime et de la base de données en cas de hit, ou pour des en-têtes de réponse qui dépendent du HTML final d’EmDash.

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

L’ordre d’exécution est le suivant :

  1. Le middleware externe s’exécute jusqu’à await next().
  2. EmDash initialise son runtime et sa base de données, puis exécute les middleware de configuration, d’authentification et de contexte de requête.
  3. La route Astro est rendue.
  4. EmDash applique les mutations de réponse, y compris le HTML d’édition visuelle et les en-têtes de sécurité/d’horodatage.
  5. next() se résout vers le middleware externe avec cette réponse finale.

Avant d’appeler next(), le middleware dispose du contexte de requête Astro et d’exécution de plateforme habituels, mais locals.emdash, locals.user, la base de données et l’état EmDash limité à la requête ne sont pas disponibles. Une Response anticipée contourne entièrement EmDash ; elle doit donc inclure les en-têtes de sécurité et de cache nécessaires. Une fois next() résolu, il est sûr de finaliser les nonces CSP, de mettre en cache le corps complet ou de définir Content-Length. Si le middleware modifie le corps, supprimez ou recalculez tout en-tête Content-Length existant.

Le hook utilise l’API middleware d’Astro sur Node et Cloudflare. Cet exemple minimal de l’API Cloudflare Cache met en cache uniquement les réponses HTML anonymes et renvoie les hits avant l’initialisation d’EmDash :

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

Sur Node, utilisez la même forme de middleware avec un cache compatible Node, comme Redis. Les clés de cache et les règles de contournement doivent inclure toute propriété de requête qui modifie la réponse rendue.

playground

Optionnel. Active le middleware utilisé par les playgrounds EmDash jetables basés sur le navigateur. Il crée une base de données Durable Object inscriptible pour chaque session, applique le seed configuré et connecte le visiteur en tant qu’administrateur anonyme avant l’exécution du middleware EmDash habituel.

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

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

Ce mode requiert @emdash-cms/cloudflare et une liaison Durable Object. Il contourne les middleware de configuration et d’authentification habituels ; utilisez-le uniquement pour des sites de démonstration éphémères, pas pour un CMS de production.

plugins

Optionnel. Tableau de plugins qui s’exécutent dans le même processus que le site Astro. Les plugins natifs appartiennent ici. Un plugin compatible sandbox peut aussi s’exécuter ici si vous lui accordez un accès processus complet et n’avez pas besoin d’isolation.

L’exemple suivant enregistre un plugin natif :

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

plugins: [seoPlugin()];

Les plugins natifs peuvent utiliser directement les API serveur et framework ; ils ne peuvent pas être déplacés vers sandboxed sauf si le paquet fournit aussi un point d’entrée de plugin compatible sandbox. Consultez Choisir un format de plugin pour les différences de création et de déploiement.

sandboxed

Optionnel. Tableau de plugins compatibles sandbox qui utilisent les API de plugin déclarées d’EmDash et s’exécutent dans des runtimes isolés. Ne placez pas un plugin natif ici : le code natif peut dépendre d’un accès processus et framework que le sandbox ne fournit pas.

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

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

Les plugins sandboxed sont ignorés lorsqu’aucun sandbox runner utilisable n’est configuré. Consultez Sandbox de plugins pour la configuration des runners Cloudflare et Node.js.

sandboxRunner

Optionnel. Spécificateur de module de la fabrique qui démarre les runtimes de plugin isolés. Il est requis pour les plugins dans sandboxed et pour les plugins marketplace ou registre.

Sur Cloudflare Workers, utilisez l’adaptateur sandbox() :

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

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

Les déploiements Node.js utilisent le module runner workerd documenté dans Sandbox de plugins : Node.js.

sandbox

Optionnel. Contrôle si un sandbox runner configuré isole les plugins. Le sandboxing est activé lorsque sandboxRunner est configuré. Définissez sandbox: false uniquement pour diagnostiquer si un problème provient d’un plugin ou de son runtime sandbox :

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

Avec false, les plugins déclarés dans sandboxed et installés depuis le marketplace s’exécutent dans le processus serveur principal sans isolation ni limites de ressources. Rétablissez le sandboxing après le diagnostic.

registry

Optionnel. Configure l’agrégateur et la politique du registre de plugins. Sans valeur explicite, EmDash utilise https://registry.emdashcms.com lorsque sandboxRunner est configuré et que sandbox n’est pas false.

Définissez registry: false pour désactiver la découverte du registre et les plugins installés via le registre, tout en conservant le sandbox runner pour les plugins déclarés dans sandboxed et les plugins Marketplace hérités :

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

Passez l’URL du service de registre sous forme de chaîne, ou utilisez un objet lorsque le site a besoin de sources de modération ou d’une politique d’âge de release. L’exemple suivant utilise la forme objet :

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"],
		},
	},
});
OptionTypeDescription
aggregatorUrlstringURL de base du service de registre. Utilisez HTTPS en production.
acceptLabelersstringIdentifiants décentralisés (DID) optionnels, séparés par des virgules, pour les services de modération acceptés par la requête. Un DID est un identifiant stable de compte Atmosphere. Ce paramètre ne peut pas remplacer la politique du service de registre.
policy.minimumReleaseAgestring | numberRetient les releases plus récentes que cet âge. Chaîne de durée ("48h", "7d") ou secondes.
policy.minimumReleaseAgeExcludestring[]DID d’éditeurs ou paires <did>/<plugin-slug> exemptées de la rétention.

La politique d’âge de release exempte la première release d’un paquet uniquement lorsque le registre signale une release conservée et confirme qu’il a observé le paquet de façon continue. Un paquet rempli rétroactivement, une release antérieure supprimée ou des preuves d’historique manquantes maintiennent la rétention. Les exemptions explicites d’éditeur et de paquet s’appliquent quel que soit l’historique.

Consultez Le registre de plugins pour le flux d’installation et le modèle de confiance.

marketplace

Obsolète. URL de base utilisée pour mettre à jour les plugins installés depuis le Marketplace hérité. La navigation Marketplace et les nouvelles installations ne sont pas affichées dans l’admin. Les plugins Marketplace existants restent modifiables et désinstallables tant que cette option est configurée.

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

Les URL de production doivent utiliser HTTPS ; HTTP n’est accepté que pour localhost et 127.0.0.1 en développement. Conservez cette option jusqu’à ce que chaque plugin Marketplace ait été remplacé ou désinstallé, puis supprimez-la. Suivez Migrer depuis le Marketplace pour la procédure complète.

fonts

Optionnel. Configuration des polices de l’interface d’administration.

Par défaut, EmDash charge Noto Sans via l’API Font d’Astro. Les polices sont téléchargées depuis Google au build et auto-hébergées, sans requêtes CDN à l’exécution. La police de base couvre les écritures latine, cyrillique, grecque, devanagari et vietnamienne.

Pour ajouter la prise en charge d’autres systèmes d’écriture, passez des noms de scripts. L’exemple suivant ajoute l’arabe et le japonais :

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

Les scripts disponibles sont 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 et tibetan.

Chaque script correspond à la variante Noto Sans associée sur Google Fonts (par ex. "arabic" charge Noto Sans Arabic). Toutes les fontes partagent un seul nom font-family et utilisent unicode-range pour que le navigateur ne télécharge que les fichiers nécessaires aux caractères de la page.

Définissez false pour désactiver entièrement l’injection de polices et utiliser les polices système :

emdash({
	fonts: false,
})

Le CSS de l’admin utilise la variable CSS --font-emdash, définie automatiquement par la configuration de polices ci-dessus.

auth

Optionnel. Un adaptateur d’authentification. La connexion intégrée d’EmDash repose sur les passkeys ; définir auth les remplace par un fournisseur externe. L’adaptateur Cloudflare Access, access(), est fourni par @emdash-cms/cloudflare :

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

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

Options pour access() :

OptionTypeDefaultDescription
teamDomainstringrequiredDomaine d’équipe Cloudflare Access
audiencestring—Tag Application Audience (AUD). Sur Workers, préférez audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variable d’environnement pour lire le tag audience à l’exécution
autoProvisionbooleantrueCréer un utilisateur EmDash à la première connexion
defaultRolenumber30Niveau de rôle pour les utilisateurs non correspondus par roleMapping (voir Rôles utilisateur)
syncRolesbooleanfalseRéappliquer roleMapping à chaque connexion au lieu de seulement au provisionnement
roleMappingobject—Associe les noms de groupes IdP aux niveaux de rôle EmDash ; la première correspondance l’emporte

authProviders

Optionnel. Un tableau de fournisseurs de connexion branchables (niveau supérieur, aux côtés de auth). Chaque entrée est le résultat de l’appel d’une fabrique de fournisseur, comme ci-dessous :

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()],
});

Fournisseurs intégrés :

  • github() — lit EMDASH_OAUTH_GITHUB_CLIENT_ID / EMDASH_OAUTH_GITHUB_CLIENT_SECRET (ou repli sans préfixe).
  • google() — lit EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • atproto() — connexion compte Atmosphere (Bluesky et le réseau AT Protocol au sens large). Aucune variable d’environnement requise. Accepte { allowedDIDs, allowedHandles, defaultRole }. Consultez le guide de connexion Atmosphere.

Des paquets tiers peuvent enregistrer leurs propres fournisseurs avec la même forme AuthProviderDescriptor — consultez Fournisseurs de connexion.

mcp

Optionnel. Active le point de terminaison Model Context Protocol (MCP) à /_emdash/api/mcp. Le point de terminaison est activé par défaut et requiert un jeton bearer ; l’activer n’accorde pas l’accès anonyme.

Définissez l’option sur false lorsque le site ne doit pas exposer de point de terminaison MCP :

emdash({
	mcp: false,
});

Consultez Référence du serveur MCP pour la création de jetons et la configuration client.

siteUrl

Origine publique orientée navigateur du site (schéma + hôte + port optionnel, sans chemin). Définissez-la avant d’exécuter la configuration de production. Seuls les hôtes de développement loopback peuvent terminer la configuration sans origine configurée.

Derrière un proxy inverse terminant TLS, Astro.url renvoie l’adresse interne (http://localhost:4321) au lieu de l’adresse publique (https://cms.example.com). Cela casse les passkeys, la correspondance d’origine CSRF, les redirections OAuth, les redirections de connexion, la découverte MCP, les exports d’instantanés, le sitemap, robots.txt et les données structurées JSON-LD. Définissez siteUrl pour corriger tout cela en une fois.

L’intégration valide cette valeur au chargement : elle doit être une URL valide avec le protocole http: ou https: et est normalisée en origin (le chemin est supprimé).

L’exemple suivant définit l’origine publique :

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

Lorsque siteUrl n’est pas défini dans la config, EmDash vérifie les variables d’environnement dans l’ordre : EMDASH_SITE_URL, puis SITE_URL. Utile pour les déploiements conteneurisés où l’URL publique est définie à l’exécution.

La configuration échoue avec SITE_URL_REQUIRED sur un hôte non loopback lorsqu’aucune source n’est définie. Cela empêche la première requête de configuration non authentifiée de choisir l’origine utilisée dans les e-mails d’authentification ultérieurs.

Sur Cloudflare Workers, le repli par variable d’environnement lit process.env. Avec nodejs_compat, Cloudflare remplit process.env par défaut pour les dates de compatibilité à partir du 2025-04-01. Les projets épinglés à une date antérieure doivent aussi ajouter nodejs_compat_populate_process_env.

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

allowedOrigins

Optionnel. Origines navigateur supplémentaires acceptées par la vérification passkey pour un déploiement accessible sous plusieurs noms d’hôte.

siteUrl définit une seule origine canonique. Lorsque le même déploiement EmDash est joignable sous plusieurs noms d’hôte partageant un domaine parent enregistrable (par ex. https://example.com et https://preview.example.com), la vérification passkey rejette les assertions dont l’origine ne correspond pas exactement à siteUrl — même si WebAuthn autorise des passkeys valides entre sous-domaines sous le même rpId.

Déclarez des origines supplémentaires acceptées via allowedOrigins dans astro.config.mjs ou la variable d’environnement EMDASH_ALLOWED_ORIGINS. L’origine canonique siteUrl reste la source de rpId ; les entrées listées ici sont acceptées au moment de la vérification. Les deux sources sont fusionnées à l’exécution : la config peut déclarer les origines stables (versionnées, revues en code) tandis que l’env ajoute des extras propres à l’environnement (par ex. aperçus PR éphémères).

L’exemple suivant déclare une origine supplémentaire dans la config :

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

Les valeurs équivalentes peuvent aussi provenir de variables d’environnement :

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

EmDash valide ces valeurs pour éviter une config morte que le navigateur n’honorerait jamais :

  • Chaque entrée doit être une URL http: ou https: analysable, sans point final et sans étiquette vide dans le nom d’hôte.
  • Lorsque allowedOrigins n’est pas vide, siteUrl doit être défini (quelle que soit la source) et ne doit pas être un littéral IP ni un nom d’hôte avec point final.
  • Chaque origine doit être le même nom d’hôte que siteUrl ou un sous-domaine de celui-ci. (WebAuthn exige que rpId soit un suffixe enregistrable de chaque origine.)

En cas d’échec de validation, vous verrez une erreur attribuée à la source comme 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.

L’endroit où l’erreur apparaît dépend de l’endroit où les valeurs sont déclarées :

  • Au démarrage d’Astro, lorsque config.allowedOrigins et config.siteUrl proviennent de astro.config.mjs — les fautes de frappe dans le code font échouer le build.
  • À la première vérification passkey, lorsqu’une valeur provient de EMDASH_ALLOWED_ORIGINS ou EMDASH_SITE_URL — les incohérences d’env apparaissent en 500 à la première tentative de vérification.

Configuration du proxy inverse

Astro ne reflète X-Forwarded-* que lorsque l’hôte public est autorisé. Configurez security.allowedDomains pour le nom d’hôte (et les schémas) que vos utilisateurs utilisent. En astro dev, ajoutez des vite.server.allowedHosts correspondants pour que Vite accepte l’en-tête Host du proxy.

Corrigez d’abord allowedDomains (et les en-têtes transférés) ; utilisez siteUrl lorsque l’URL reconstruite diverge encore de l’origine du navigateur (typique lorsque TLS est terminé en amont et que la requête upstream reste en http://).

Avec TLS en amont, lier le serveur de dev au loopback (astro dev --host 127.0.0.1) suffit souvent : le proxy se connecte localement tandis que siteUrl correspond à l’origine HTTPS publique.

Si votre proxy écrit un en-tête IP client, définissez trustedProxyHeaders pour que les limites de débit d’EmDash utilisent la vraie IP client au lieu de regrouper chaque requête sous une clé partagée « unknown ».

La configuration suivante définit allowedDomains, vite.server.allowedHosts et siteUrl ensemble pour un déploiement derrière proxy inverse :

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

Optionnel. En-têtes à faire confiance pour la résolution de l’IP client lorsque vous êtes derrière un proxy inverse que vous contrôlez. Utilisé par les limites de débit d’auth (magic link, inscription, passkey, flux appareil OAuth) et le point de terminaison public de commentaires.

Sur Cloudflare, l’objet cf attaché à la requête est utilisé automatiquement — vous n’avez normalement pas besoin de définir ceci. Sur des déploiements auto-hébergés derrière nginx, Caddy, Traefik, Fly, Railway ou similaire, définissez l’en-tête que votre proxy écrit pour que les limites de débit regroupent par IP client réelle au lieu de traiter chaque requête comme « unknown ».

L’exemple suivant fait confiance à l’en-tête x-real-ip défini par nginx, Caddy ou Traefik :

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

Les en-têtes sont essayés dans l’ordre. Les valeurs correspondant à *-forwarded-for sont analysées comme listes séparées par des virgules et la première entrée est utilisée. L’exemple suivant préfère l’en-tête Fly.io et retombe sur x-forwarded-for :

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

Lorsque ce n’est pas défini dans la config, EmDash lit la variable d’environnement EMDASH_TRUSTED_PROXY_HEADERS (séparée par des virgules). Un tableau vide explicite dans la config remplace la variable d’environnement.

maxUploadSize

Optionnel. Taille maximale autorisée de téléversement de fichier média en octets. S’applique aux téléversements multipart directs et aux téléversements par URL signée. Par défaut 52_428_800 (50 Mo). L’exemple suivant porte la limite à 100 Mo :

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
ValueDescription
number (bytes)Doit être un entier fini positif
omittedPar défaut 50 Mo

Les téléversements dépassant la limite configurée sont rejetés avec une réponse 413 Payload Too Large sur le chemin de téléversement direct, ou 400 Validation Error sur le chemin URL signée.

admin

Optionnel. Remplace l’image de marque EmDash dans l’interface d’administration. Ces valeurs ne modifient pas le titre, le logo ou le favicon du site public.

emdash({
	admin: {
		logo: "/images/agency-logo.webp",
		siteName: "Agency CMS",
		favicon: "/favicon.ico",
	},
});
OptionTypeDescription
logostringURL ou chemin du logo pour la page de connexion et la barre latérale
siteNamestringNom affiché dans la barre latérale et le titre du navigateur
faviconstringURL ou chemin du favicon pour les pages admin

toolbar

Optionnel. Contrôle la façon dont la barre d’outils de l’éditeur (la pastille flottante sur les pages publiques) est livrée. Par défaut "server".

ValueBehavior
"server" (default)La barre d’outils est injectée côté serveur dans chaque réponse HTML rendue pour un éditeur authentifié.
"client"Le HTML public est identique pour chaque visiteur. Un petit script bootstrap affiche une pastille « Edit » dans les navigateurs connectés à l’admin ; un clic vérifie la session et recharge la page avec un paramètre de requête _edit, toujours rendue à neuf (jamais en cache) avec la barre complète.
falseNe jamais rendre la barre d’outils ni le script bootstrap.
emdash({
	toolbar: "client",
})

Utilisez "client" lorsque votre HTML public est servi via un cache partagé (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Avec l’injection côté serveur, un éditeur parcourant le site public reçoit la variante anonyme en cache — sans barre d’outils — dès qu’un visiteur anonyme a rempli le cache en premier ; la barre apparaît et disparaît donc selon l’état du cache. En mode client, rien de spécifique à la session n’est injecté dans le HTML partageable ; le cache reste pleinement efficace et la barre d’outils est fiable.

Notes sur le mode "client" :

  • Les visiteurs déconnectés qui ouvrent une URL partagée ?_edit sont redirigés vers l’URL canonique, afin que le paramètre ne fuite pas de brouillons ni ne remplisse des entrées de cache supplémentaires avec le contenu de la page.
  • Le signal « connecté » est un indicateur localStorage non secret défini par l’admin ; la pastille vérifie la vraie session avant d’entrer en mode édition.
  • Le bootstrap est un petit <script> inline. Si votre site envoie une Content-Security-Policy stricte sans 'unsafe-inline', ajoutez un hash — il en va de même pour la barre injectée côté serveur.
  • EmDash n’injecte rien de spécifique à la session — mais si vos propres modèles branchent sur Astro.locals.user (par ex. un lien « Admin » pour les utilisateurs connectés), cette variance reste dans votre HTML et fragmente encore le cache.

Dans chaque mode, la barre d’outils peut être fermée dans le navigateur via son bouton × (par navigateur, jusqu’à la prochaine ouverture de l’admin par un éditeur). Les réponses en aperçu et en mode édition sont toujours rendues côté serveur avec Cache-Control: private, no-store.

experimental

Optionnel. Fonctionnalités opt-in dont le comportement ou le format sur le fil peut changer, ou être supprimé, dans une release mineure. Chaque champ est activé indépendamment.

experimental.registry

Obsolète. Utilisez l’option de niveau supérieur registry. Une configuration experimental.registry existante continue de fonctionner lorsque l’option de niveau supérieur est omise. Si les deux sont présentes, la valeur de niveau supérieur l’emporte.

Le changement suivant déplace une URL de registre existante au niveau supérieur :

emdash({
	experimental: {
		registry: "https://registry.example.com",
	},
	registry: "https://registry.example.com",
});

Adaptateurs de base de données

Importez les adaptateurs depuis emdash/db :

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

sqlite(config)

Base de données SQLite via le pilote de base de données intégré de Node.js. L’exemple suivant se connecte à un fichier local :

OptionTypeDescription
urlstringChemin de fichier avec préfixe file:
sqlite({ url: "file:./data.db" });

libsql(config)

Base de données libSQL. L’exemple suivant se connecte à une base libSQL distante :

OptionTypeDescription
urlstringURL de la base de données
authTokenstringJeton d’auth à l’exécution (optionnel pour fichiers locaux)
migrationAuthTokenEnvstringNom de la variable du jeton de migration (défaut TURSO_AUTH_TOKEN)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

Base de données PostgreSQL avec pool de connexions.

OptionTypeDescription
connectionStringstringURL de connexion PostgreSQL
hoststringHôte de la base de données
portnumberPort de la base de données
databasestringNom de la base de données
userstringUtilisateur de la base de données
passwordstringMot de passe de la base de données
sslbooleanActiver SSL
pool.minnumberTaille minimale du pool (défaut : 0)
pool.maxnumberTaille maximale du pool (défaut : 10)
pool.connectionTimeoutMillisnumberAttente maximale de connexion (défaut pg : 0, pas de timeout)
pool.idleTimeoutMillisnumberDurée de vie client inactif (défaut pg : 10 000 ms)
migrationConnectionStringEnvstringNom de la variable de chaîne de connexion de migration (défaut DATABASE_URL)

L’exemple suivant se connecte avec une chaîne de connexion :

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

d1(config)

Base de données Cloudflare D1. Import depuis @emdash-cms/cloudflare.

OptionTypeDefaultDescription
bindingstring—Nom de liaison D1 dans wrangler.jsonc
sessionstring"disabled"Mode de réplication de lecture : "disabled", "auto" ou "primary-first"
bookmarkCookiestring"__em_d1_bookmark"Nom du cookie pour les signets de session
coalescebooleanfalseRegroupe les lectures concurrentes dans le même tour de boucle d’événements ; requiert un mode de session autre que "disabled"

L’exemple suivant montre une liaison basique et une avec réplicas de lecture activées :

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

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

Lorsque session est "auto" ou "primary-first", EmDash utilise l’API D1 Sessions pour router les requêtes de lecture vers des réplicas proches. Les utilisateurs authentifiés obtiennent une cohérence read-your-writes basée sur les signets. Consultez Options de base de données — Réplicas de lecture pour les détails.

hyperdrive(config?)

PostgreSQL via une liaison Cloudflare Hyperdrive. Importez cet adaptateur depuis @emdash-cms/cloudflare.

OptionTypeDefaultDescription
bindingstring"HYPERDRIVE"Liaison Hyperdrive principale avec cache de requêtes désactivé
cachedBindingstring—Deuxième liaison optionnelle avec cache activé pour lectures publiques anonymes
preferUncachedAfterWriteMsnumber60_000Durée pendant laquelle les lectures publiques utilisent le primaire après une écriture de contenu lorsque cachedBinding est défini
migrationConnectionStringEnvstringDerived from the primary bindingVariable d’environnement contenant l’URL PostgreSQL directe utilisée par emdash migrate
maxnumber5Connexions maximales d’un isolate Worker vers Hyperdrive

L’exemple suivant route les requêtes authentifiées et les écritures via la liaison sans cache, tandis que les lectures publiques anonymes peuvent utiliser la liaison avec cache :

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

Les deux liaisons doivent pointer vers la même base de données. Installez pg version 8.16.3 ou ultérieure, activez le flag de compatibilité nodejs_compat et configurez une URL de base directe pour les migrations de déploiement. Consultez Options de base de données : Hyperdrive pour la configuration complète Worker et migration.

durableObjects(config)

Stocke le CMS dans un Durable Object SQLite. Importez cet adaptateur depuis @emdash-cms/cloudflare.

OptionTypeDefaultDescription
bindingstringrequiredLiaison de namespace Durable Object pour la classe EmDashDB
namestring"emdash"Nom d’objet singleton ; ne le modifiez que pour isoler plusieurs bases derrière une liaison
sessionstring"disabled""auto" route les lectures anonymes vers les réplicas et les écritures vers le primaire
bookmarkCookiestring"__em_do_bookmark"Cookie utilisé pour la cohérence read-your-writes en mode "auto"
durableObjects({ binding: "DB_DO", session: "auto" });

Le routage vers réplicas requiert les flags de compatibilité experimental et replica_routing, plus la classe Durable Object et les entrées de migration dans wrangler.jsonc.

previewDatabase(config)

Crée une base snapshot isolée par session d’aperçu dans un Durable Object. La seule option est le nom binding requis :

previewDatabase({ binding: "PREVIEW_DB" });

Cet adaptateur sert à l’infrastructure d’aperçu, pas comme base principale d’un site de production.

playgroundDatabase(config)

Crée une base seedée inscriptible par session de playground dans un Durable Object. Associez-le à l’option d’intégration playground :

playgroundDatabase({ binding: "PLAYGROUND_DB" });

Le binding requis identifie le namespace Durable Object du playground. Utilisez cet adaptateur uniquement pour des sites de démonstration jetables.

Adaptateurs de stockage

Importez local et s3 depuis emdash/astro. L’adaptateur r2 s’importe depuis @emdash-cms/cloudflare :

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

local(config)

Stockage sur système de fichiers local. L’exemple suivant sert les téléversements depuis un répertoire local :

OptionTypeDescription
directorystringChemin du répertoire
baseUrlstringURL de base pour servir les fichiers
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Liaison Cloudflare R2. L’exemple suivant utilise une liaison R2 avec une URL publique :

OptionTypeDescription
bindingstringNom de liaison R2
publicUrlstringURL publique optionnelle
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

Stockage compatible S3. Tous les champs de config sont optionnels : tout champ omis dans s3({...}) est résolu depuis la variable d’environnement S3_* correspondante au démarrage du processus Node. Les valeurs explicites ont toujours la priorité.

Après fusion des valeurs de config et d’environnement, endpoint et bucket sont requis. Si l’une des credentials est définie, accessKeyId et secretAccessKey sont tous deux requis. Des valeurs manquantes font échouer le démarrage avec le code d’erreur MISSING_S3_CONFIG.

Prérequis : installez @aws-sdk/client-s3 et @aws-sdk/s3-request-presigner dans votre projet. Le noyau EmDash n’inclut pas le SDK AWS. Consultez Options de stockage : stockage compatible S3 pour les détails.

OptionTypeDescription
endpointstringURL du point de terminaison S3 (S3_ENDPOINT)
bucketstringNom du bucket (S3_BUCKET)
accessKeyIdstringClé d’accès (S3_ACCESS_KEY_ID)
secretAccessKeystringClé secrète (S3_SECRET_ACCESS_KEY)
regionstringRégion, défaut "auto" (S3_REGION)
publicUrlstringURL CDN optionnelle (S3_PUBLIC_URL)

Les exemples suivants résolvent tous les champs depuis l’environnement, mélangent config et environnement, ou passent chaque champ explicitement :

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

La résolution des variables d’environnement à l’exécution est une fonctionnalité Node uniquement. Sur Cloudflare Workers, les secrets et variables sont exposés via le paramètre env du gestionnaire fetch, pas via process.env ; les variables d’environnement S3_* ne sont donc pas prises en compte. Les déploiements Workers doivent utiliser l’adaptateur r2(config) ou passer des valeurs explicites à s3({...}). Consultez Options de stockage pour les détails.

Adaptateurs de cache d’objets

Passez l’un de ceux-ci à l’option objectCache.

kvCache(config)

Backend Cloudflare KV, partagé entre tous les isolates. Import depuis @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?)

Backend in-process pour Node.js et le développement. Import depuis 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)
})

Consultez Cache d’objets pour la configuration et le comportement.

Adaptateurs d’authentification et de sandbox

Ces adaptateurs renvoient des valeurs pour les options d’intégration auth et sandboxRunner.

access(config)

Remplace la connexion passkey intégrée par l’authentification Cloudflare Access. Importez-le depuis @emdash-cms/cloudflare et passez le résultat à auth :

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

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

teamDomain est requis. L’adaptateur peut lire l’audience de l’application depuis audience ou depuis la variable nommée par audienceEnvVar ; il accepte aussi autoProvision, defaultRole, syncRoles et roleMapping. L’option auth documente leurs valeurs par défaut et le comportement des rôles.

sandbox()

Sélectionne Cloudflare Worker Loader comme sandbox runner de plugins. Importez-le depuis @emdash-cms/cloudflare et passez sa valeur de retour à sandboxRunner :

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

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

Le site a aussi besoin d’une liaison Worker Loader et du point d’entrée pont de plugins. Consultez Sandbox de plugins : Cloudflare Workers pour ces paramètres de déploiement.

Adaptateurs de fournisseurs média

Passez des descripteurs de fournisseurs média à mediaProviders. Les deux fournisseurs Cloudflare intégrés s’importent depuis @emdash-cms/cloudflare.

Chaque option *EnvVar ci-dessous nomme une variable d’environnement. Le fournisseur la lit dans cet ordre : une liaison Cloudflare Workers de ce nom, puis process.env sur l’adaptateur Node. L’option directe correspondante (accountId, accountHash, apiToken) a toujours la priorité sur les deux.

cloudflareImages(config)

Ajoute Cloudflare Images pour parcourir, téléverser, supprimer et livrer des ressources image.

OptionTypeDefaultDescription
accountIdstringFrom CF_ACCOUNT_IDID de compte Cloudflare
accountIdEnvVarstring"CF_ACCOUNT_ID"Variable utilisée lorsque accountId est omis
accountHashstringFrom CF_IMAGES_ACCOUNT_HASHHash de compte utilisé dans les URL de livraison
accountHashEnvVarstring"CF_IMAGES_ACCOUNT_HASH"Variable utilisée lorsque accountHash est omis
apiTokenstringFrom CF_IMAGES_TOKENJeton avec permissions lecture et édition Cloudflare Images
apiTokenEnvVarstring"CF_IMAGES_TOKEN"Variable utilisée lorsque apiToken est omis
deliveryDomainstringimagedelivery.netNom d’hôte de livraison d’images personnalisé
defaultVariantstring"public"Variante d’image utilisée pour l’affichage
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];

cloudflareStream(config)

Ajoute Cloudflare Stream pour parcourir, rechercher, téléverser, supprimer et lire des ressources vidéo.

OptionTypeDefaultDescription
accountIdstringFrom CF_ACCOUNT_IDID de compte Cloudflare
accountIdEnvVarstring"CF_ACCOUNT_ID"Variable utilisée lorsque accountId est omis
apiTokenstringFrom CF_STREAM_TOKENJeton avec permissions lecture et édition Cloudflare Stream
apiTokenEnvVarstring"CF_STREAM_TOKEN"Variable utilisée lorsque apiToken est omis
customerSubdomainstringCloudflare defaultNom d’hôte de livraison Stream personnalisé
controlsbooleantrueAfficher les contrôles du lecteur
autoplaybooleanfalseDémarrer la lecture automatiquement
loopbooleanfalseRépéter la lecture
mutedbooleanfalse, or true with autoplayCouper le son
mediaProviders: [cloudflareStream({ controls: true })];

Consultez Bibliothèque de médias : fournisseurs de médias pour les liaisons requises et les composants de rendu.

Adaptateur de cache Astro

cloudflareCache(config?)

L’adaptateur hérité renvoie un cache.provider Astro qui stocke les réponses dans l’API Workers Cache et purge les tags de cache via l’API REST Cloudflare :

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

export default defineConfig({
	cache: {
		provider: cloudflareCache(),
	},
});

Il accepte cacheName (défaut "emdash") et bookmarkCookie (défaut "__em_d1_bookmark"), plus zoneId ou zoneIdEnvVar et apiToken ou apiTokenEnvVar pour les requêtes de purge par tag. Les noms de variables par défaut sont CF_ZONE_ID et CF_CACHE_PURGE_TOKEN.

Collections live

Configurez le chargeur EmDash dans src/live.config.ts :

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

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

Options du chargeur

La fonction emdashLoader() ne prend aucun argument :

emdashLoader();

Variables d’environnement

EmDash respecte ces variables d’environnement :

VariableDescription
EMDASH_SITE_URLOrigine publique orientée navigateur (repli sur SITE_URL)
EMDASH_ALLOWED_ORIGINSListe séparée par des virgules d’origines supplémentaires acceptées par la vérification passkey (déploiements multi-sous-domaines).
EMDASH_DATABASE_URLRemplace l’URL de la base de données
EMDASH_ENCRYPTION_KEYClé pour chiffrer les secrets de plugins au repos. Fournie par l’opérateur — jamais stockée en base.
EMDASH_PREVIEW_SECRETRemplacement optionnel du secret HMAC d’aperçu. Si non défini, une valeur stable par site est générée et stockée en base.
EMDASH_IP_SALTRemplacement optionnel du sel de hash IP commentateur. Si non défini, une valeur stable par site est générée et stockée en base.
EMDASH_AUTH_SECRETHérité. Utilisé comme source de sel IP si défini ; les installations existantes doivent le conserver pour préserver des hashes IP commentateur stables après mise à jour.
EMDASH_TURNSTILE_SECRET_KEYClé secrète Cloudflare Turnstile (repli sur TURNSTILE_SECRET_KEY). Lorsqu’elle est définie, les soumissions de commentaires doivent inclure un jeton Turnstile valide — associez-la à la prop turnstileSiteKey sur <CommentForm>.
EMDASH_URLURL EmDash distante pour la synchronisation de schéma

Générez une clé de chiffrement avec la commande suivante :

npx emdash secrets generate

Configuration package.json

Les modèles et sites peuvent déclarer des métadonnées optionnelles sous une clé emdash dans package.json :

{
	"emdash": {
		"label": "My Blog Template",
		"schema": ".emdash/schema.sql",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
OptionDescription
labelNom du modèle pour l’affichage
schemaSchéma SQL optionnel lu par emdash init
seedChemin vers le fichier JSON de seed
urlURL distante utilisée par le flux obsolète emdash dev --types

Configuration TypeScript

Pendant le développement local, l’intégration Astro génère emdash-env.d.ts à la racine du projet et le rafraîchit après les changements de schéma. Le fichier augmente le module emdash, de sorte que les imports standard getEmDashCollection() et getEmDashEntry() infèrent les champs de collection locale sans alias de chemin.

La commande séparée emdash types récupère le schéma d’une instance locale ou distante en cours d’exécution et écrit .emdash/types.ts par défaut. Ajoutez un alias uniquement lorsque le code applicatif importe directement cette sortie autonome :

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

Générez les types de schéma distant autonomes avec la commande suivante :

npx emdash types