Référence de Configuration

Sur cette page

EmDash est configuré via deux fichiers : astro.config.mjs pour l’intégration et src/live.config.ts pour les collections de contenu.

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

Requis. 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 (importer depuis @emdash-cms/cloudflare)
database: d1({ binding: "DB" });

Voir Options de base de données pour plus de détails.

storage

Requis. Configuration de l’adaptateur de stockage de médias. Choisissez un adaptateur :

// Système de fichiers local (développement)
storage: local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

// Binding R2 (Cloudflare Workers)
storage: r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev", // optionnel
});

// Compatible S3 (toute plateforme) — tous les champs depuis les variables d'environnement S3_*
storage: s3()

// Ou avec des valeurs explicites
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", // optionnel, par défaut : "auto"
	publicUrl: "https://cdn.example.com", // optionnel
});

Voir Options de stockage pour plus de détails.

objectCache

Optionnel. Met en cache les résultats des 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é lorsqu’omis. Choisissez un adaptateur :

// Cloudflare KV (partagé entre tous les isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });

// En mémoire (Node.js / développement)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();

Voir Object Cache pour la configuration et les options.

plugins

Optionnel. Tableau de plugins EmDash. L’exemple suivant enregistre un plugin :

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

plugins: [seoPlugin()];

fonts

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

Par défaut, EmDash charge Noto Sans via l’API Astro Font. Les polices sont téléchargées depuis Google au moment de la compilation et auto-hébergées, il n’y a donc pas de requêtes CDN en temps d’exécution. La police de base couvre les scripts latin, cyrillique, grec, devanagari et vietnamien.

Pour ajouter le support de systèmes d’écriture supplémentaires, 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 est associé à la variante Noto Sans correspondante sur Google Fonts (ex. "arabic" charge Noto Sans Arabic). Toutes les faces de police partagent un seul nom font-family et utilisent unicode-range pour que le navigateur ne télécharge que les fichiers nécessaires pour les 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 admin utilise la variable CSS --font-emdash. Elle est définie automatiquement par la configuration des polices ci-dessus.

auth

Optionnel. Un adaptateur d’authentification. La connexion intégrée d’EmDash utilise les passkeys ; configurer 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() :

OptionTypePar défautDescription
teamDomainstringrequisVotre domaine d’équipe Cloudflare Access
audiencestringTag Application Audience (AUD). Sur Workers, préférez audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variable d’environnement pour lire le tag d’audience au moment de l’exécution
autoProvisionbooleantrueCréer un utilisateur EmDash à la première connexion
defaultRolenumber30Niveau de rôle pour les utilisateurs non mappés par roleMapping (voir Rôles utilisateur)
syncRolesbooleanfalseRéappliquer roleMapping à chaque connexion au lieu du seul provisionnement
roleMappingobjectMapper les noms de groupes IdP aux niveaux de rôle EmDash ; première correspondance gagne

authProviders

Optionnel. Un tableau de fournisseurs de connexion enfichables (niveau supérieur, à côté de auth). Chaque entrée est le résultat de l’appel d’une factory de fournisseur :

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 les fallbacks sans préfixe).
  • google() — lit EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • atproto() — Connexion de compte Atmosphere (Bluesky et le réseau AT Protocol élargi). Aucune variable d’environnement nécessaire. Accepte { allowedDIDs, allowedHandles, defaultRole }. Voir le guide de connexion Atmosphere.

Les packages tiers peuvent enregistrer leurs propres fournisseurs en utilisant la même forme AuthProviderDescriptor — voir Fournisseurs de connexion.

siteUrl

Optionnel. L’origine publique côté navigateur pour le site (schéma + hôte + port optionnel, pas de chemin).

Derrière un proxy inverse terminant TLS, Astro.url retourne l’adresse interne (http://localhost:4321) au lieu de la 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 de snapshots, le sitemap, robots.txt et les données structurées JSON-LD. Définissez siteUrl pour corriger tout cela d’un coup.

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

Quand siteUrl n’est pas défini dans la configuration, EmDash vérifie les variables d’environnement dans l’ordre : EMDASH_SITE_URL, puis SITE_URL. Utile pour les déploiements en conteneurs où l’URL publique est définie au moment de l’exécution.

Sur Cloudflare Workers, le fallback de variable d’environnement lit process.env, qui est vide sauf si le flag de compatibilité nodejs_compat_populate_process_env est activé. Pour utiliser la variable d’environnement au lieu de l’option de configuration, définissez les deux :

// wrangler.jsonc
{
	"compatibility_flags": ["nodejs_compat", "nodejs_compat_populate_process_env"],
	"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}

Vérification de passkey multi-origine

siteUrl définit une seule origine canonique. Quand le même déploiement EmDash est accessible sous plusieurs noms d’hôte partageant un domaine parent enregistrable (ex. https://example.com et https://preview.example.com), la vérification de passkey rejette les assertions dont l’origine ne correspond pas exactement à siteUrl — même si WebAuthn permet aux passkeys d’être valides entre les 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. Le siteUrl canonique reste la source du rpId ; les entrées listées ici sont acceptées lors de la vérification. Les deux sources sont fusionnées au moment de l’exécution.

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

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

Les valeurs équivalentes peuvent aussi provenir des 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 prévenir les configurations mortes que le navigateur ne respecterait jamais :

  • Chaque entrée doit être une URL http: ou https: analysable sans point final et sans étiquettes vides dans le nom d’hôte.
  • Quand allowedOrigins n’est pas vide, siteUrl doit être défini (depuis n’importe quelle source) et ne doit pas être un littéral IP ou avoir 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.

Quand la validation échoue, vous verrez une erreur attribuée à la source.

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

  • Au démarrage d’Astro, quand config.allowedOrigins et config.siteUrl viennent de astro.config.mjs — les coquilles dans le code font échouer la compilation.
  • À la première vérification de passkey, quand l’une des valeurs vient de EMDASH_ALLOWED_ORIGINS ou EMDASH_SITE_URL — les discordances d’environnement apparaissent comme des 500s lors de la première tentative de vérification.

Configuration de proxy inverse

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

Préférez corriger allowedDomains (et les en-têtes transmis) d’abord ; utilisez siteUrl quand l’URL reconstruite diverge encore de l’origine du navigateur (typique quand TLS est terminé devant et la requête en amont reste en http://).

Avec TLS devant, lier le serveur de développement à la boucle locale (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 d’IP client, définissez trustedProxyHeaders pour que les limites de débit d’EmDash puissent utiliser l’IP réelle du client.

La configuration suivante définit allowedDomains, vite.server.allowedHosts et siteUrl ensemble pour un déploiement avec 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 auxquels faire confiance pour la résolution d’IP client lors de l’exécution derrière un proxy inverse que vous contrôlez. Utilisé par les limites de débit d’auth (magic-link, inscription, passkey, OAuth device flow) et le point de terminaison public des commentaires.

Sur Cloudflare, l’objet cf attaché à la requête est utilisé automatiquement — vous n’avez normalement pas besoin de le définir. Sur les déploiements auto-hébergés derrière nginx, Caddy, Traefik, Fly, Railway ou similaires, définissez ceci à l’en-tête que votre proxy écrit.

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 des listes séparées par des virgules et la première entrée est utilisée :

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

Quand non défini dans la configuration, EmDash lit la variable d’environnement EMDASH_TRUSTED_PROXY_HEADERS (séparée par des virgules). Un tableau vide explicite dans la configuration remplace la variable d’environnement.

maxUploadSize

Optionnel. Taille maximale autorisée pour le téléchargement de fichiers médias en octets. S’applique aux téléchargements multipart directs et aux téléchargements avec URL signée. Par défaut 52_428_800 (50 Mo). L’exemple suivant élève 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 Mo
});

toolbar

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

ValeurComportement
"server" (par défaut)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 ; cliquer vérifie la session et recharge la page avec un paramètre de requête _edit, qui est toujours rendu frais (jamais mis en cache) avec la barre d’outils complète.
falseNe jamais rendre la barre d’outils ni le script bootstrap.
emdash({
	toolbar: "client",
})

Utilisez "client" quand votre HTML public est servi via un cache partagé (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …).

experimental

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

experimental.registry

Optionnel. Pointe les flux de navigation et d’installation de plugins du tableau de bord admin vers un registre de plugins fédéré au lieu du marketplace central. Nécessite sandboxRunner, car les plugins du registre s’exécutent en sandbox.

emdash({
	sandboxRunner: "@emdash-cms/sandbox-cloudflare",
	experimental: {
		registry: {
			aggregatorUrl: "https://registry.emdashcms.com",
			acceptLabelers: "did:plc:emdashverification",
			policy: {
				minimumReleaseAge: "48h",
				minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
			},
		},
	},
});
OptionTypeDescription
aggregatorUrlstringOrigine de l’agrégateur où les endpoints XRPC du registre sont montés. HTTPS en production.
acceptLabelersstringDIDs d’étiqueteurs séparés par des virgules transmis à l’agrégateur pour les étiquettes de retrait et de vérification.
policy.minimumReleaseAgestring | numberRetenir les releases plus récents que cet âge. Chaîne de durée ("48h", "7d") ou secondes.
policy.minimumReleaseAgeExcludestring[]DIDs (ou paires <did>/<slug>) exemptés de la rétention.

Voir Le registre de plugins pour le workflow complet.

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 utilisant better-sqlite3.

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

libsql(config)

Base de données libSQL.

OptionTypeDescription
urlstringURL de la base de données
authTokenstringToken d’auth (optionnel pour les fichiers locaux)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

Base de données PostgreSQL avec pooling 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 min. du pool (défaut : 0)
pool.maxnumberTaille max. du pool (défaut : 10)
postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

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

OptionTypePar défautDescription
bindingstringNom du binding D1 de 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
d1({ binding: "DB" });
d1({ binding: "DB", session: "auto" });

Adaptateurs de stockage

Importez local et s3 depuis emdash/astro. L’adaptateur r2 est importé 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.

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

r2(config)

Binding Cloudflare R2.

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

s3(config?)

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

Prérequis : installez @aws-sdk/client-s3 et @aws-sdk/s3-request-presigner dans votre projet. Le core EmDash ne bundle pas le SDK AWS. Voir Options de stockage : Stockage compatible S3 pour les détails.

OptionTypeDescription
endpointstringURL de l’endpoint 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)
s3()
s3({ publicUrl: "https://cdn.example.com" })
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",
})

Adaptateurs d’object cache

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

kvCache(config)

Backend Cloudflare KV, partagé entre tous les isolates. Importer depuis @emdash-cms/cloudflare.

kvCache({
	binding: "CACHE",
	defaultTtl: 3600,
	revalidate: 1000,
	timeout: 2000,
	keyPrefix: "em",
})

memoryCache(config?)

Backend en processus pour Node.js et développement. Importer depuis emdash/astro.

memoryCache({
	defaultTtl: 3600,
	revalidate: 1000,
	maxEntries: 1000,
	keyPrefix: "em",
})

Voir Object Cache pour la configuration et le comportement.

Collections en direct

Configurez le loader 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 loader

La fonction emdashLoader() ne prend aucun argument :

emdashLoader();

Variables d’environnement

EmDash respecte ces variables d’environnement :

VariableDescription
EMDASH_SITE_URLOrigine publique côté navigateur (recourt à SITE_URL)
EMDASH_ALLOWED_ORIGINSListe séparée par des virgules d’origines supplémentaires acceptées par la vérification de passkey (déploiements multi-sous-domaine).
EMDASH_DATABASE_URLRemplacer l’URL de la base de données
EMDASH_ENCRYPTION_KEYClé pour chiffrer les secrets des plugins au repos. Fournie par l’opérateur — jamais stockée dans la base de données.
EMDASH_PREVIEW_SECRETRemplacement optionnel pour le secret HMAC de prévisualisation.
EMDASH_IP_SALTRemplacement optionnel pour le sel du hash IP du commentateur.
EMDASH_AUTH_SECRETHérité. Utilisé comme source de sel IP si défini. Les nouvelles installations ne devraient pas le définir.
EMDASH_TURNSTILE_SECRET_KEYClé secrète Cloudflare Turnstile (recourt à TURNSTILE_SECRET_KEY).
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 templates et sites peuvent déclarer des métadonnées optionnelles sous une clé emdash dans package.json :

{
	"emdash": {
		"label": "My Blog Template",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
OptionDescription
labelNom du template pour l’affichage
seedChemin vers le fichier seed JSON
urlURL distante pour la synchronisation de schéma

Configuration TypeScript

EmDash génère les types dans .emdash/types.ts. Ajoutez un alias de chemin à votre tsconfig.json :

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

Générez les types avec la commande suivante :

npx emdash types