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 é:
- O middleware externo executa até
await next(). - O EmDash inicializa runtime e banco de dados, depois executa middleware de setup, autenticação e contexto de solicitação.
- A rota Astro renderiza.
- O EmDash aplica mutações de resposta, incluindo HTML de edição visual e cabeçalhos de segurança/timing.
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ção | Tipo | Descrição |
|---|---|---|
aggregatorUrl | string | URL base do serviço de registro. Use HTTPS em produção. |
acceptLabelers | string | Identificadores 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.minimumReleaseAge | string | number | Retém releases mais novos que esta idade. String de duração ("48h", "7d") ou segundos. |
policy.minimumReleaseAgeExclude | string[] | 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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
teamDomain | string | obrigatório | Domínio da equipe Cloudflare Access |
audience | string | — | Tag Application Audience (AUD). No Workers, prefira audienceEnvVar. |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | Variável de ambiente para ler a tag de audience em runtime |
autoProvision | boolean | true | Criar usuário EmDash no primeiro login |
defaultRole | number | 30 | Nível de função para usuários não correspondidos por roleMapping (veja Funções de usuário) |
syncRoles | boolean | false | Reaplicar roleMapping a cada login em vez de apenas no provisionamento |
roleMapping | object | — | 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:ouhttps:analisável, sem ponto final e sem rótulos vazios no hostname. - Quando
allowedOriginsnão está vazio,siteUrldeve estar definido (qualquer fonte) e não deve ser literal IP nem hostname com ponto final. - Cada origem deve ser o mesmo hostname que
siteUrlou um subdomínio dele. (WebAuthn exige querpIdseja 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.allowedOriginseconfig.siteUrlvêm deastro.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_ORIGINSouEMDASH_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
});
| Valor | Descrição |
|---|---|
number (bytes) | Deve ser inteiro finito positivo |
| omitido | Padrã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ção | Tipo | Descrição |
|---|---|---|
logo | string | URL ou path do logo na página de login e sidebar |
siteName | string | Nome exibido na sidebar e no título do navegador |
favicon | string | URL 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".
| Valor | Comportamento |
|---|---|
"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. |
false | Nunca 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
?_editsã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
localStoragenã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 enviaContent-Security-Policyestrita 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ção | Tipo | Descrição |
|---|---|---|
url | string | Caminho 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ção | Tipo | Descrição |
|---|---|---|
url | string | URL do banco de dados |
authToken | string | Token de auth em runtime (opcional para arquivos locais) |
migrationAuthTokenEnv | string | Nome 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ção | Tipo | Descrição |
|---|---|---|
connectionString | string | URL de conexão PostgreSQL |
host | string | Host do banco |
port | number | Porta do banco |
database | string | Nome do banco |
user | string | Usuário do banco |
password | string | Senha do banco |
ssl | boolean | Habilitar SSL |
pool.min | number | Tamanho mínimo do pool (padrão: 0) |
pool.max | number | Tamanho máximo do pool (padrão: 10) |
pool.connectionTimeoutMillis | number | Espera máxima de conexão (pg padrão: 0, sem timeout) |
pool.idleTimeoutMillis | number | Tempo de vida do cliente ocioso (pg padrão: 10.000 ms) |
migrationConnectionStringEnv | string | Nome 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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
binding | string | — | Nome do binding D1 em wrangler.jsonc |
session | string | "disabled" | Modo de replicação de leitura: "disabled", "auto" ou "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | Nome do cookie para bookmarks de sessão |
coalesce | boolean | false | Agrupa 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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Binding Hyperdrive principal com cache de consultas desabilitado |
cachedBinding | string | — | Segundo binding opcional com cache habilitado para leituras públicas anônimas |
preferUncachedAfterWriteMs | number | 60_000 | Por quanto tempo leituras públicas usam o primário após escrita de conteúdo quando cachedBinding está definido |
migrationConnectionStringEnv | string | Derivado do binding principal | Variável de ambiente com a URL PostgreSQL direta usada por emdash migrate |
max | number | 5 | Má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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
binding | string | obrigatório | Binding de namespace Durable Object para a classe EmDashDB |
name | string | "emdash" | Nome do objeto singleton; altere apenas para isolar vários bancos atrás de um binding |
session | string | "disabled" | "auto" roteia leituras anônimas para réplicas e gravações para o primário |
bookmarkCookie | string | "__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ção | Tipo | Descrição |
|---|---|---|
directory | string | Caminho do diretório |
baseUrl | string | URL 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ção | Tipo | Descrição |
|---|---|---|
binding | string | Nome do binding R2 |
publicUrl | string | URL 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ção | Tipo | Descrição |
|---|---|---|
endpoint | string | URL do endpoint S3 (S3_ENDPOINT) |
bucket | string | Nome do bucket (S3_BUCKET) |
accessKeyId | string | Access key (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Secret key (S3_SECRET_ACCESS_KEY) |
region | string | Região, padrão "auto" (S3_REGION) |
publicUrl | string | URL 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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
accountId | string | De CF_ACCOUNT_ID | ID da conta Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variável usada quando accountId é omitido |
accountHash | string | De CF_IMAGES_ACCOUNT_HASH | Hash da conta usado nas URLs de entrega |
accountHashEnvVar | string | "CF_IMAGES_ACCOUNT_HASH" | Variável usada quando accountHash é omitido |
apiToken | string | De CF_IMAGES_TOKEN | Token com permissões de leitura e edição do Cloudflare Images |
apiTokenEnvVar | string | "CF_IMAGES_TOKEN" | Variável usada quando apiToken é omitido |
deliveryDomain | string | imagedelivery.net | Hostname personalizado de entrega de imagens |
defaultVariant | string | "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ção | Tipo | Padrão | Descrição |
|---|---|---|---|
accountId | string | De CF_ACCOUNT_ID | ID da conta Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variável usada quando accountId é omitido |
apiToken | string | De CF_STREAM_TOKEN | Token com permissões de leitura e edição do Cloudflare Stream |
apiTokenEnvVar | string | "CF_STREAM_TOKEN" | Variável usada quando apiToken é omitido |
customerSubdomain | string | Padrão Cloudflare | Hostname personalizado de entrega do Stream |
controls | boolean | true | Exibir controles do player |
autoplay | boolean | false | Iniciar reprodução automaticamente |
loop | boolean | false | Repetir reprodução |
muted | boolean | false, ou true com autoplay | Silenciar 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ável | Descrição |
|---|---|
EMDASH_SITE_URL | Origem pública voltada ao navegador (fallback para SITE_URL) |
EMDASH_ALLOWED_ORIGINS | Lista separada por vírgulas de origens adicionais aceitas pela verificação de passkey (implantações multi-subdomínio). |
EMDASH_DATABASE_URL | Substitui a URL do banco de dados |
EMDASH_ENCRYPTION_KEY | Chave para criptografar segredos de plugins em repouso. Fornecida pelo operador — nunca armazenada no banco de dados. |
EMDASH_PREVIEW_SECRET | Substituiçã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_SALT | Substituiçã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_SECRET | Legado. 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_KEY | Chave 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_URL | URL 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ção | Descrição |
|---|---|
label | Nome do template para exibição |
schema | Esquema SQL opcional lido por emdash init |
seed | Caminho para arquivo JSON de seed |
url | URL 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