Referência de configuração

Nesta página

A configuração principal do EmDash fica em astro.config.mjs, enquanto src/live.config.ts registra o carregador de conteúdo. Valores específicos de implantação também podem vir de variáveis de ambiente. Um pequeno bloco de metadados emdash em package.json suporta rótulos de template e fluxos CLI locais legados.

Integração Astro

Configure o EmDash como integração Astro em 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: [],
		}),
	],
});

Opções de integração

database

Obrigatório. Configuração do adaptador de banco de dados. Escolha um adaptador:

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

Consulte Opções de banco de dados para detalhes.

migrations

Opcional. Controla o tratamento em runtime das migrações internas de banco de dados do EmDash. Omitir esta opção usa o padrão { runtime: "auto" }.

migrations: {
	runtime: "check", // "auto" | "check" | "manual"
	dev: "auto",     // substituição opcional em desenvolvimento
}

auto verifica e aplica migrações pendentes, check retorna 503 quando há migrações pendentes conhecidas pelo build em execução, e manual não executa consulta de migração em runtime. EMDASH_MIGRATIONS_MODE substitui o modo de runtime efetivo. Consulte Gerenciar migrações do banco de dados principal antes de adotar check ou manual.

storage

Opcional. Configuração do adaptador de armazenamento de mídia. Quando omitida, o EmDash armazena arquivos em ./.emdash/uploads e os serve por /_emdash/api/media/file. Escolha um adaptador quando o diretório local padrão não for adequado:

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

Consulte Opções de armazenamento para detalhes.

images

Opcional. Controla se o EmDash integra mídia armazenada com a otimização de imagens do Astro. O padrão é true.

Quando habilitado, o EmDash envolve o endpoint de imagens do Astro para que <Image> e getImage() leiam os bytes de origem diretamente do adaptador de armazenamento configurado. Isso também funciona quando a URL original da mídia está atrás do Cloudflare Access. Defina images: false quando outro serviço de imagens tratar a mídia ou quando cada imagem deve ser renderizada sem o wrapper de endpoint do EmDash.

emdash({
	images: false,
});

mediaProviders

Opcional. Adiciona serviços de mídia à biblioteca de mídia. O provedor local baseado em armazenamento permanece disponível automaticamente; cada descritor neste array adiciona outro lugar onde editores podem navegar ou enviar mídia.

O exemplo a seguir adiciona Cloudflare Images e Cloudflare Stream:

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

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

As credenciais dos provedores são resolvidas em runtime. As configurações vazias acima usam as variáveis de ambiente padrão do Cloudflare descritas nas seções dos adaptadores cloudflareImages(config) e cloudflareStream(config). Consulte Biblioteca de mídia: provedores de mídia para configuração de bindings e renderização.

objectCache

Opcional. Armazena em cache resultados de consultas de conteúdo e configuração em um armazenamento chave/valor, para que leituras sejam atendidas sem consultar o banco de dados a cada solicitação. Desabilitado quando omitido. Escolha um adaptador:

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

Consulte Cache de objetos para configuração e opções.

middleware.outer

Opcional. Registra um módulo de middleware Astro fora da pilha completa de middleware do EmDash. Como a integração o registra no Astro com order: "pre", ele também executa antes do middleware definido em src/middleware.ts. Use-o para portões de solicitação ou caches de resposta completa que devem evitar inicialização de runtime e banco de dados em um hit, ou para cabeçalhos de resposta que dependem do HTML final do EmDash.

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

A ordem de execução é:

  1. O middleware externo executa até await next().
  2. O EmDash inicializa runtime e banco de dados, depois executa middleware de setup, autenticação e contexto de solicitação.
  3. A rota Astro renderiza.
  4. O EmDash aplica mutações de resposta, incluindo HTML de edição visual e cabeçalhos de segurança/timing.
  5. next() resolve para o middleware externo com essa resposta final.

Antes de chamar next(), o middleware tem o contexto normal de solicitação Astro e de execução da plataforma, mas locals.emdash, locals.user, o banco de dados e o estado EmDash escopado à solicitação não estão disponíveis. Uma Response antecipada ignora o EmDash por completo, então deve incluir os cabeçalhos de segurança e cache necessários. Depois que next() resolve, é seguro finalizar nonces CSP, cachear o corpo completo ou definir Content-Length. Se o middleware alterar o corpo, remova ou recalcule qualquer cabeçalho Content-Length existente.

O hook usa a API de middleware do Astro tanto no Node quanto no Cloudflare. Este exemplo mínimo da Cloudflare Cache API cacheia apenas respostas HTML anônimas e retorna hits antes da inicialização do 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;
});

No Node, use a mesma forma de middleware com um cache compatível com Node, como Redis. Chaves de cache e regras de bypass devem incluir toda propriedade de solicitação que altera a resposta renderizada.

playground

Opcional. Habilita o middleware usado por playgrounds EmDash descartáveis baseados em navegador. Cria um banco de dados Durable Object gravável por sessão, aplica o seed configurado e autentica o visitante como administrador anônimo antes do middleware EmDash normal executar.

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

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

Este modo exige @emdash-cms/cloudflare e um binding Durable Object. Ignora o middleware normal de setup e autenticação; use-o apenas para sites de demonstração efêmeros, não para um CMS de produção.

plugins

Opcional. Array de plugins que executam no mesmo processo do site Astro. Plugins nativos pertencem aqui. Um plugin compatível com sandbox também pode executar aqui quando você confia nele com acesso total ao processo e não precisa de isolamento.

O exemplo a seguir registra um plugin nativo:

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

plugins: [seoPlugin()];

Plugins nativos podem usar APIs de servidor e framework diretamente, então não podem ser movidos para sandboxed a menos que o pacote também forneça um ponto de entrada de plugin compatível com sandbox. Consulte Escolher um formato de plugin para diferenças de autoria e implantação.

sandboxed

Opcional. Array de plugins compatíveis com sandbox que usam as APIs de plugin declaradas do EmDash e executam em runtimes isolados. Não coloque um plugin nativo aqui: código nativo pode depender de acesso a processo e framework que o sandbox não fornece.

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

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

Plugins em sandbox são ignorados quando nenhum sandbox runner utilizável está configurado. Consulte Sandbox de plugins para configuração do runner no Cloudflare e Node.js.

sandboxRunner

Opcional. Especificador de módulo da factory que inicia runtimes de plugin isolados. É obrigatório para plugins em sandboxed e para plugins do marketplace ou registro.

No Cloudflare Workers, use o adaptador sandbox():

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

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

Implantações Node.js usam o módulo runner workerd documentado em Sandbox de plugins: Node.js.

sandbox

Opcional. Controla se um sandbox runner configurado isola plugins. O sandboxing é habilitado quando sandboxRunner está configurado. Defina sandbox: false apenas para diagnosticar se um problema vem do plugin ou do runtime de sandbox:

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

Com false, plugins declarados em sandboxed e instalados do marketplace executam no processo principal do servidor sem isolamento ou limites de recursos. Restaure o sandboxing após o diagnóstico.

registry

Opcional. Configura o agregador e a política do registro de plugins. Sem valor explícito, o EmDash usa https://registry.emdashcms.com quando sandboxRunner está configurado e sandbox não é false.

Defina registry: false para desabilitar descoberta do registro e plugins instalados pelo registro, mantendo o sandbox runner disponível para plugins declarados em sandboxed e plugins legados do Marketplace:

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

Passe a URL do serviço de registro como string, ou use um objeto quando o site precisar de fontes de moderação ou política de idade de release. O exemplo a seguir usa a forma de objeto:

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"],
		},
	},
});
OpçãoTipoDescrição
aggregatorUrlstringURL base do serviço de registro. Use HTTPS em produção.
acceptLabelersstringIdentificadores descentralizados (DIDs) opcionais, separados por vírgula, de serviços de moderação aceitos pela solicitação. Um DID é um identificador estável de conta Atmosphere. Esta configuração não pode substituir a política do serviço de registro.
policy.minimumReleaseAgestring | numberRetém releases mais novos que esta idade. String de duração ("48h", "7d") ou segundos.
policy.minimumReleaseAgeExcludestring[]DIDs de publicadores ou pares <did>/<plugin-slug> isentos da retenção.

A política de idade de release isenta o primeiro release de um pacote apenas quando o registro reporta um release retido e confirma que observou o pacote continuamente. Pacote backfilled, release anterior excluído ou evidência de histórico ausente mantêm a retenção. Isenções explícitas de publicador e pacote aplicam-se independentemente do histórico.

Consulte O registro de plugins para fluxo de instalação e modelo de confiança.

marketplace

Descontinuado. URL base usada para atualizar plugins instalados do Marketplace legado. Navegação e novas instalações do Marketplace não aparecem no admin. Plugins existentes do Marketplace permanecem atualizáveis e desinstaláveis enquanto esta opção estiver configurada.

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

URLs de produção devem usar HTTPS; HTTP é aceito apenas para localhost e 127.0.0.1 durante o desenvolvimento. Mantenha esta opção até que todo plugin do Marketplace tenha sido substituído ou desinstalado, depois remova-a. Siga Migrar do Marketplace para o procedimento completo.

fonts

Opcional. Configuração de fontes da UI do admin.

Por padrão, o EmDash carrega Noto Sans via a Astro Font API. As fontes são baixadas do Google no build e auto-hospedadas, sem solicitações CDN em runtime. A fonte base cobre scripts latino, cirílico, grego, devanagari e vietnamita.

Para adicionar suporte a sistemas de escrita adicionais, passe nomes de scripts. O exemplo a seguir adiciona árabe e japonês:

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

Os scripts disponíveis são 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 e tibetan.

Cada script mapeia para a variante Noto Sans correspondente no Google Fonts (por exemplo, "arabic" carrega Noto Sans Arabic). Todas as faces compartilham um único nome font-family e usam unicode-range para o navegador baixar apenas os arquivos necessários aos caracteres da página.

Defina false para desabilitar totalmente a injeção de fontes e usar fontes do sistema:

emdash({
	fonts: false,
})

O CSS do admin usa a variável CSS --font-emdash, definida automaticamente pela configuração de fontes acima.

auth

Opcional. Um adaptador de autenticação. O login integrado do EmDash usa passkeys; definir auth substitui passkeys por um provedor externo. O adaptador Cloudflare Access, access(), é fornecido por @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,
		},
	}),
});

Opções de access():

OpçãoTipoPadrãoDescrição
teamDomainstringobrigatórioDomínio da equipe Cloudflare Access
audiencestring—Tag Application Audience (AUD). No Workers, prefira audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variável de ambiente para ler a tag de audience em runtime
autoProvisionbooleantrueCriar usuário EmDash no primeiro login
defaultRolenumber30Nível de função para usuários não correspondidos por roleMapping (veja Funções de usuário)
syncRolesbooleanfalseReaplicar roleMapping a cada login em vez de apenas no provisionamento
roleMappingobject—Mapeia nomes de grupos IdP para níveis de função EmDash; a primeira correspondência vence

authProviders

Opcional. Array de provedores de login plugáveis (nível superior, junto com auth). Cada entrada é o resultado de chamar uma factory de provedor, como abaixo:

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

Provedores integrados:

  • github() — lê EMDASH_OAUTH_GITHUB_CLIENT_ID / EMDASH_OAUTH_GITHUB_CLIENT_SECRET (ou fallbacks sem prefixo).
  • google() — lê EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • atproto() — login com conta Atmosphere (Bluesky e a rede AT Protocol mais ampla). Não exige variáveis de ambiente. Aceita { allowedDIDs, allowedHandles, defaultRole }. Consulte o guia de login Atmosphere.

Pacotes de terceiros podem registrar seus próprios provedores usando a mesma forma AuthProviderDescriptor — veja Provedores de login.

mcp

Opcional. Habilita o endpoint Model Context Protocol (MCP) em /_emdash/api/mcp. O endpoint é habilitado por padrão e exige bearer token; habilitá-lo não concede acesso anônimo.

Defina a opção como false quando o site não deve expor um endpoint MCP:

emdash({
	mcp: false,
});

Consulte Referência do servidor MCP para criação de token e configuração de cliente.

siteUrl

Origem pública voltada ao navegador do site (esquema + host + porta opcional, sem path). Defina-a antes de executar o setup de produção. Apenas hosts de desenvolvimento loopback podem concluir o setup sem origem configurada.

Atrás de um proxy reverso com terminação TLS, Astro.url retorna o endereço interno (http://localhost:4321) em vez do público (https://cms.example.com). Isso quebra passkeys, correspondência de origem CSRF, redirects OAuth, redirects de login, descoberta MCP, exportações de snapshot, sitemap, robots.txt e dados estruturados JSON-LD. Defina siteUrl para corrigir tudo de uma vez.

A integração valida este valor no carregamento: deve ser uma URL válida com protocolo http: ou https: e é normalizada para origin (o path é removido).

O exemplo a seguir define a origem pública:

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

Quando siteUrl não está definido na config, o EmDash verifica variáveis de ambiente nesta ordem: EMDASH_SITE_URL, depois SITE_URL. Isso é útil para implantações em contêiner onde a URL pública é definida em runtime.

O setup falha com SITE_URL_REQUIRED em host não loopback quando nenhuma fonte está definida. Isso impede que a primeira solicitação de setup não autenticada escolha a origem usada em e-mails de autenticação posteriores.

No Cloudflare Workers, o fallback por variável de ambiente lê process.env. Com nodejs_compat, o Cloudflare popula process.env por padrão para datas de compatibilidade em ou após 2025-04-01. Projetos fixados em data anterior também devem adicionar 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

Opcional. Origens adicionais de navegador aceitas pela verificação de passkey para uma implantação disponível em mais de um hostname.

siteUrl define uma única origem canônica. Quando a mesma implantação EmDash é acessível por vários hostnames que compartilham um domínio pai registrável (por exemplo, https://example.com e https://preview.example.com), a verificação de passkey rejeita asserções cuja origem não corresponde exatamente a siteUrl — mesmo que WebAuthn permita passkeys válidas entre subdomínios sob o mesmo rpId.

Declare origens adicionais aceitas via allowedOrigins em astro.config.mjs ou a variável de ambiente EMDASH_ALLOWED_ORIGINS. A siteUrl canônica permanece a fonte de rpId; entradas listadas aqui são aceitas na verificação. As duas fontes são mescladas em runtime, para a config declarar origens estáveis (versionadas, revisadas em código) enquanto o env adiciona extras específicos do ambiente (por exemplo, previews efêmeros de PR).

O exemplo a seguir declara uma origem extra na config:

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

Valores equivalentes também podem vir de variáveis de ambiente:

EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validação

O EmDash valida estes valores para evitar config morta que o navegador nunca honraria:

  • Cada entrada deve ser uma URL http: ou https: analisável, sem ponto final e sem rótulos vazios no hostname.
  • Quando allowedOrigins não está vazio, siteUrl deve estar definido (qualquer fonte) e não deve ser literal IP nem hostname com ponto final.
  • Cada origem deve ser o mesmo hostname que siteUrl ou um subdomínio dele. (WebAuthn exige que rpId seja sufixo registrável de toda origem.)

Quando a validação falha, você verá um erro atribuído à fonte como 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.

Onde o erro aparece depende de onde os valores são declarados:

  • Na inicialização do Astro, quando config.allowedOrigins e config.siteUrl vêm de astro.config.mjs — erros de digitação no código falham o build.
  • Na primeira verificação de passkey, quando qualquer valor vem de EMDASH_ALLOWED_ORIGINS ou EMDASH_SITE_URL — incompatibilidades de env aparecem como 500 na primeira tentativa de verificação.

Configuração de proxy reverso

O Astro só reflete X-Forwarded-* quando o host público é permitido. Configure security.allowedDomains para o hostname (e esquemas) que seus usuários acessam. Em astro dev, adicione vite.server.allowedHosts correspondentes para o Vite aceitar o cabeçalho Host do proxy.

Prefira corrigir allowedDomains (e cabeçalhos encaminhados) primeiro; use siteUrl quando a URL reconstruída ainda divergir da origem do navegador (típico quando TLS termina na frente e a solicitação upstream permanece http://).

Com TLS na frente, vincular o servidor de dev ao loopback (astro dev --host 127.0.0.1) costuma ser suficiente: o proxy conecta localmente enquanto siteUrl corresponde à origem HTTPS pública.

Se o proxy grava um cabeçalho de IP do cliente, defina trustedProxyHeaders para os rate limits do EmDash usarem o IP real do cliente em vez de agrupar toda solicitação sob uma chave compartilhada “unknown”.

A configuração a seguir define allowedDomains, vite.server.allowedHosts e siteUrl juntos para implantação com proxy reverso:

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

Opcional. Cabeçalhos confiáveis para resolução de IP do cliente ao executar atrás de um proxy reverso que você controla. Usado por rate limits de auth (magic-link, signup, passkey, fluxo de dispositivo OAuth) e pelo endpoint público de comentários.

No Cloudflare, o objeto cf anexado à solicitação é usado automaticamente — normalmente não é necessário definir isto. Em implantações self-hosted atrás de nginx, Caddy, Traefik, Fly, Railway ou similar, defina o cabeçalho que seu proxy grava para rate limits agruparem por IP real do cliente em vez de tratar toda solicitação como “unknown”.

O exemplo a seguir confia no cabeçalho x-real-ip definido por nginx, Caddy ou Traefik:

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

Os cabeçalhos são tentados em ordem. Valores que correspondem a *-forwarded-for são analisados como listas separadas por vírgula e a primeira entrada é usada. O exemplo a seguir prefere o cabeçalho do Fly.io e faz fallback para x-forwarded-for:

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

Quando não definido na config, o EmDash lê a variável de ambiente EMDASH_TRUSTED_PROXY_HEADERS (separada por vírgulas). Um array vazio explícito na config substitui a variável de ambiente.

maxUploadSize

Opcional. Tamanho máximo permitido de upload de arquivo de mídia em bytes. Aplica-se a uploads multipart diretos e uploads por URL assinada. Padrão 52_428_800 (50 MB). O exemplo a seguir eleva o limite para 100 MB:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
ValorDescrição
number (bytes)Deve ser inteiro finito positivo
omitidoPadrão 50 MB

Uploads que excedem o limite configurado são rejeitados com resposta 413 Payload Too Large no caminho de upload direto, ou 400 Validation Error no caminho de URL assinada.

admin

Opcional. Substitui a marca EmDash na interface do admin. Estes valores não alteram título, logo ou favicon do site público.

emdash({
	admin: {
		logo: "/images/agency-logo.webp",
		siteName: "Agency CMS",
		favicon: "/favicon.ico",
	},
});
OpçãoTipoDescrição
logostringURL ou path do logo na página de login e sidebar
siteNamestringNome exibido na sidebar e no título do navegador
faviconstringURL ou path do favicon nas páginas do admin

toolbar

Opcional. Controla como a barra de ferramentas do editor (o pill flutuante nas páginas públicas) é entregue. Padrão "server".

ValorComportamento
"server" (padrão)A barra é injetada no servidor em toda resposta HTML renderizada para um editor autenticado.
"client"O HTML público é idêntico para todo visitante. Um pequeno script bootstrap mostra um pill “Edit” em navegadores que fizeram login no admin; ao clicar, verifica a sessão e recarrega a página com parâmetro de query _edit, sempre renderizada fresca (nunca em cache) com a barra completa.
falseNunca renderiza a barra nem o script bootstrap.
emdash({
	toolbar: "client",
})

Use "client" quando seu HTML público é servido por cache compartilhado (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Com injeção no servidor, um editor navegando o site público recebe a variante anônima em cache — sem a barra — sempre que um visitante anônimo preencheu o cache primeiro, então a barra aparece e desaparece conforme o estado do cache. No modo client, nada específico de sessão é injetado no HTML compartilhável, o cache permanece totalmente efetivo e a barra é confiável.

Notas sobre o modo "client":

  • Visitantes deslogados que abrem uma URL compartilhada ?_edit são redirecionados para a URL canônica, para o parâmetro não vazar rascunhos nem preencher entradas extras de cache com conteúdo da página.
  • O sinal de “logado” é uma flag localStorage não secreta definida pelo admin; o pill verifica a sessão real antes de entrar na visão de edição.
  • O bootstrap é um <script> inline pequeno. Se seu site envia Content-Security-Policy estrita sem 'unsafe-inline', adicione um hash — o mesmo vale para a barra injetada no servidor.
  • O EmDash não injeta nada específico de sessão — mas se seus templates ramificam em Astro.locals.user (por exemplo, link “Admin” para usuários logados), essa variação ainda está no HTML e ainda fragmenta o cache.

Em todo modo, a barra pode ser dispensada no navegador pelo botão × (por navegador, até a próxima vez que um editor abrir o admin). Respostas de preview e modo de edição sempre renderizam no servidor com Cache-Control: private, no-store.

experimental

Opcional. Recursos opt-in cujo comportamento ou formato wire pode mudar ou ser removido em release minor. Cada campo é habilitado independentemente.

experimental.registry

Descontinuado. Use a opção de nível superior registry. Configuração experimental.registry existente continua funcionando quando a opção de nível superior é omitida. Se ambas estiverem presentes, o valor de nível superior prevalece.

A alteração a seguir move uma URL de registro existente para o nível superior:

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

Adaptadores de banco de dados

Importe os adaptadores de emdash/db:

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

sqlite(config)

Banco SQLite usando o driver de banco integrado do Node.js. O exemplo a seguir conecta a um arquivo local:

OpçãoTipoDescrição
urlstringCaminho de arquivo com prefixo file:
sqlite({ url: "file:./data.db" });

libsql(config)

Banco libSQL. O exemplo a seguir conecta a um banco libSQL remoto:

OpçãoTipoDescrição
urlstringURL do banco de dados
authTokenstringToken de auth em runtime (opcional para arquivos locais)
migrationAuthTokenEnvstringNome da variável do token de migração (padrão TURSO_AUTH_TOKEN)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

Banco PostgreSQL com pool de conexões.

OpçãoTipoDescrição
connectionStringstringURL de conexão PostgreSQL
hoststringHost do banco
portnumberPorta do banco
databasestringNome do banco
userstringUsuário do banco
passwordstringSenha do banco
sslbooleanHabilitar SSL
pool.minnumberTamanho mínimo do pool (padrão: 0)
pool.maxnumberTamanho máximo do pool (padrão: 10)
pool.connectionTimeoutMillisnumberEspera máxima de conexão (pg padrão: 0, sem timeout)
pool.idleTimeoutMillisnumberTempo de vida do cliente ocioso (pg padrão: 10.000 ms)
migrationConnectionStringEnvstringNome da variável da connection string de migração (padrão DATABASE_URL)

O exemplo a seguir conecta com connection string:

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

d1(config)

Banco Cloudflare D1. Importe de @emdash-cms/cloudflare.

OpçãoTipoPadrãoDescrição
bindingstring—Nome do binding D1 em wrangler.jsonc
sessionstring"disabled"Modo de replicação de leitura: "disabled", "auto" ou "primary-first"
bookmarkCookiestring"__em_d1_bookmark"Nome do cookie para bookmarks de sessão
coalescebooleanfalseAgrupa leituras concorrentes no mesmo turno do event loop; exige modo de sessão diferente de "disabled"

O exemplo a seguir mostra um binding básico e outro com réplicas de leitura habilitadas:

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

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

Quando session é "auto" ou "primary-first", o EmDash usa a D1 Sessions API para rotear consultas de leitura para réplicas próximas. Usuários autenticados obtêm consistência read-your-writes baseada em bookmark. Consulte Opções de banco de dados — Réplicas de leitura para detalhes.

hyperdrive(config?)

PostgreSQL por um binding Cloudflare Hyperdrive. Importe este adaptador de @emdash-cms/cloudflare.

OpçãoTipoPadrãoDescrição
bindingstring"HYPERDRIVE"Binding Hyperdrive principal com cache de consultas desabilitado
cachedBindingstring—Segundo binding opcional com cache habilitado para leituras públicas anônimas
preferUncachedAfterWriteMsnumber60_000Por quanto tempo leituras públicas usam o primário após escrita de conteúdo quando cachedBinding está definido
migrationConnectionStringEnvstringDerivado do binding principalVariável de ambiente com a URL PostgreSQL direta usada por emdash migrate
maxnumber5Máximo de conexões de um isolate Worker ao Hyperdrive

O exemplo a seguir roteia solicitações autenticadas e gravações pelo binding sem cache, enquanto leituras públicas anônimas podem usar o binding com cache:

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

Ambos os bindings devem apontar para o mesmo banco. Instale pg versão 8.16.3 ou posterior, habilite a flag de compatibilidade nodejs_compat e configure uma URL de banco direta para migrações de implantação. Consulte Opções de banco de dados: Hyperdrive para configuração completa de Worker e migração.

durableObjects(config)

Armazena o CMS em um Durable Object com SQLite. Importe este adaptador de @emdash-cms/cloudflare.

OpçãoTipoPadrãoDescrição
bindingstringobrigatórioBinding de namespace Durable Object para a classe EmDashDB
namestring"emdash"Nome do objeto singleton; altere apenas para isolar vários bancos atrás de um binding
sessionstring"disabled""auto" roteia leituras anônimas para réplicas e gravações para o primário
bookmarkCookiestring"__em_do_bookmark"Cookie usado para consistência read-your-writes no modo "auto"
durableObjects({ binding: "DB_DO", session: "auto" });

O roteamento de réplicas exige as flags de compatibilidade experimental e replica_routing, além da classe Durable Object e entradas de migração em wrangler.jsonc.

previewDatabase(config)

Cria um banco snapshot isolado por sessão de preview em um Durable Object. A única opção é o nome binding obrigatório:

previewDatabase({ binding: "PREVIEW_DB" });

Este adaptador é para infraestrutura de preview, não para o banco principal de um site de produção.

playgroundDatabase(config)

Cria um banco seeded gravável por sessão de playground em um Durable Object. Combine com a opção de integração playground:

playgroundDatabase({ binding: "PLAYGROUND_DB" });

O binding obrigatório identifica o namespace Durable Object do playground. Use este adaptador apenas para sites de demonstração descartáveis.

Adaptadores de armazenamento

Importe local e s3 de emdash/astro. O adaptador r2 é importado de @emdash-cms/cloudflare:

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

local(config)

Armazenamento em filesystem local. O exemplo a seguir serve uploads de um diretório local:

OpçãoTipoDescrição
directorystringCaminho do diretório
baseUrlstringURL base para servir arquivos
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Binding Cloudflare R2. O exemplo a seguir usa um binding R2 com URL pública:

OpçãoTipoDescrição
bindingstringNome do binding R2
publicUrlstringURL pública opcional
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

Armazenamento compatível com S3. Todos os campos de config são opcionais: qualquer campo omitido em s3({...}) é resolvido da variável de ambiente S3_* correspondente quando o processo Node inicia. Valores explícitos sempre têm precedência.

Depois que config e ambiente são mesclados, endpoint e bucket são obrigatórios. Se qualquer credencial for definida, accessKeyId e secretAccessKey são obrigatórios. Valores ausentes falham na inicialização com o código de erro MISSING_S3_CONFIG.

Pré-requisito: instale @aws-sdk/client-s3 e @aws-sdk/s3-request-presigner no seu projeto. O core do EmDash não inclui o AWS SDK. Consulte Opções de armazenamento: armazenamento compatível com S3 para detalhes.

OpçãoTipoDescrição
endpointstringURL do endpoint S3 (S3_ENDPOINT)
bucketstringNome do bucket (S3_BUCKET)
accessKeyIdstringAccess key (S3_ACCESS_KEY_ID)
secretAccessKeystringSecret key (S3_SECRET_ACCESS_KEY)
regionstringRegião, padrão "auto" (S3_REGION)
publicUrlstringURL CDN opcional (S3_PUBLIC_URL)

Os exemplos a seguir resolvem todos os campos do ambiente, misturam config e ambiente ou passam cada campo explicitamente:

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

A resolução de variáveis de ambiente em runtime é recurso apenas do Node. No Cloudflare Workers, secrets e variáveis são expostos pelo parâmetro env do handler fetch, não por process.env, então variáveis de ambiente S3_* não são capturadas. Implantações Workers devem usar o adaptador r2(config) ou passar valores explícitos para s3({...}). Consulte Opções de armazenamento para detalhes.

Adaptadores de cache de objetos

Passe um destes para a opção objectCache.

kvCache(config)

Backend Cloudflare KV, compartilhado entre todos os isolates. Importe de @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 para Node.js e desenvolvimento. Importe de 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)
})

Consulte Cache de objetos para configuração e comportamento.

Adaptadores de autenticação e sandbox

Estes adaptadores retornam valores para as opções de integração auth e sandboxRunner.

access(config)

Substitui o login passkey integrado por autenticação Cloudflare Access. Importe de @emdash-cms/cloudflare e passe o resultado para auth:

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

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

teamDomain é obrigatório. O adaptador pode ler a audience da aplicação de audience ou da variável nomeada por audienceEnvVar; também aceita autoProvision, defaultRole, syncRoles e roleMapping. A opção auth documenta seus padrões e comportamento de funções.

sandbox()

Seleciona Cloudflare Worker Loader como sandbox runner de plugins. Importe de @emdash-cms/cloudflare e passe o valor retornado para sandboxRunner:

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

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

O site também precisa de um binding Worker Loader e do ponto de entrada ponte de plugins. Consulte Sandbox de plugins: Cloudflare Workers para essas configurações de implantação.

Adaptadores de provedores de mídia

Passe descritores de provedores de mídia para mediaProviders. Ambos os provedores Cloudflare integrados são importados de @emdash-cms/cloudflare.

Cada opção *EnvVar abaixo nomeia uma variável de ambiente. O provedor lê nesta ordem: um binding Cloudflare Workers com esse nome e, em seguida, process.env no adaptador Node. A opção direta correspondente (accountId, accountHash, apiToken) sempre tem precedência sobre ambas.

cloudflareImages(config)

Adiciona Cloudflare Images para navegar, enviar, excluir e entregar recursos de imagem.

OpçãoTipoPadrãoDescrição
accountIdstringDe CF_ACCOUNT_IDID da conta Cloudflare
accountIdEnvVarstring"CF_ACCOUNT_ID"Variável usada quando accountId é omitido
accountHashstringDe CF_IMAGES_ACCOUNT_HASHHash da conta usado nas URLs de entrega
accountHashEnvVarstring"CF_IMAGES_ACCOUNT_HASH"Variável usada quando accountHash é omitido
apiTokenstringDe CF_IMAGES_TOKENToken com permissões de leitura e edição do Cloudflare Images
apiTokenEnvVarstring"CF_IMAGES_TOKEN"Variável usada quando apiToken é omitido
deliveryDomainstringimagedelivery.netHostname personalizado de entrega de imagens
defaultVariantstring"public"Variante de imagem usada na exibição
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];

cloudflareStream(config)

Adiciona Cloudflare Stream para navegar, pesquisar, enviar, excluir e reproduzir recursos de vídeo.

OpçãoTipoPadrãoDescrição
accountIdstringDe CF_ACCOUNT_IDID da conta Cloudflare
accountIdEnvVarstring"CF_ACCOUNT_ID"Variável usada quando accountId é omitido
apiTokenstringDe CF_STREAM_TOKENToken com permissões de leitura e edição do Cloudflare Stream
apiTokenEnvVarstring"CF_STREAM_TOKEN"Variável usada quando apiToken é omitido
customerSubdomainstringPadrão CloudflareHostname personalizado de entrega do Stream
controlsbooleantrueExibir controles do player
autoplaybooleanfalseIniciar reprodução automaticamente
loopbooleanfalseRepetir reprodução
mutedbooleanfalse, ou true com autoplaySilenciar reprodução
mediaProviders: [cloudflareStream({ controls: true })];

Consulte Biblioteca de mídia: provedores de mídia para os bindings necessários e componentes de renderização.

Adaptador de cache Astro

cloudflareCache(config?)

O adaptador legado retorna um cache.provider Astro que armazena respostas na Workers Cache API e purga tags de cache pela API REST da Cloudflare:

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

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

Aceita cacheName (padrão "emdash") e bookmarkCookie (padrão "__em_d1_bookmark"), além de zoneId ou zoneIdEnvVar e apiToken ou apiTokenEnvVar para solicitações de purge por tag. Os nomes padrão das variáveis são CF_ZONE_ID e CF_CACHE_PURGE_TOKEN.

Coleções live

Configure o loader EmDash em src/live.config.ts:

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

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

Opções do loader

A função emdashLoader() não recebe argumentos:

emdashLoader();

Variáveis de ambiente

O EmDash respeita estas variáveis de ambiente:

VariávelDescrição
EMDASH_SITE_URLOrigem pública voltada ao navegador (fallback para SITE_URL)
EMDASH_ALLOWED_ORIGINSLista separada por vírgulas de origens adicionais aceitas pela verificação de passkey (implantações multi-subdomínio).
EMDASH_DATABASE_URLSubstitui a URL do banco de dados
EMDASH_ENCRYPTION_KEYChave para criptografar segredos de plugins em repouso. Fornecida pelo operador — nunca armazenada no banco de dados.
EMDASH_PREVIEW_SECRETSubstituição opcional do segredo HMAC de preview. Quando não definido, um valor estável por site é gerado e armazenado no banco de dados.
EMDASH_IP_SALTSubstituição opcional do salt de hash do IP do comentarista. Quando não definido, um valor estável por site é gerado e armazenado no banco de dados.
EMDASH_AUTH_SECRETLegado. Usado como fonte do salt de IP se definido; instalações existentes devem mantê-lo para preservar hashes estáveis de IP de comentaristas após atualização.
EMDASH_TURNSTILE_SECRET_KEYChave secreta Cloudflare Turnstile (fallback para TURNSTILE_SECRET_KEY). Quando definida, envios de comentários devem incluir um token Turnstile válido — combine com a prop turnstileSiteKey em <CommentForm>.
EMDASH_URLURL remota do EmDash para sincronização de esquema

Gere uma chave de criptografia com o seguinte comando:

npx emdash secrets generate

Configuração do package.json

Templates e sites podem declarar metadados opcionais sob a chave emdash em package.json:

{
	"emdash": {
		"label": "My Blog Template",
		"schema": ".emdash/schema.sql",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
OpçãoDescrição
labelNome do template para exibição
schemaEsquema SQL opcional lido por emdash init
seedCaminho para arquivo JSON de seed
urlURL remota usada pelo fluxo descontinuado emdash dev --types

Configuração TypeScript

Durante o desenvolvimento local, a integração Astro gera emdash-env.d.ts na raiz do projeto e o atualiza após mudanças de esquema. O arquivo aumenta o módulo emdash, para que os imports padrão getEmDashCollection() e getEmDashEntry() infiram campos de coleção local sem alias de path.

O comando separado emdash types busca o esquema de uma instância local ou remota em execução e grava .emdash/types.ts por padrão. Adicione um alias apenas quando o código da aplicação importar essa saída standalone diretamente:

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

Gere os tipos standalone de esquema remoto com o seguinte comando:

npx emdash types