Ir al contenido

Runbook de branding SSR

Owner: maintainers de LEMN UI y apps consumidoras Revision: 2026-07-17 Patterns: PAT-UI-BRAND-CONTRACT-001, PAT-UI-SSR-BRANDING-001, PAT-SEC-SECRETS-001, PAT-TEST-EVIDENCE-001

Un browser de produccion nunca renderiza un componente sin branding ni el default crudo del provider. Resuelve, autoriza, verifica e inyecta un mode de una BrandingVersion publicada antes del primer byte HTML. Hydration confirma el projectionHash, mode ID y mode hash exactos; no repara branding despues del paint.

El server deriva el Workspace desde identidad de workload o routing confiable. Un Workspace ID del browser no es autoridad. El mode solo puede seleccionarse si las proyecciones firmadas lo declaran permitido. Un host Cloudflare de la misma cuenta usa un RPC Service Binding hacia un WorkerEntrypoint dedicado. Sus ctx.props estaticos, definidos por deployment, autentican workspaceId, consumerId y el permiso branding:resolve; la llamada RPC no lleva una credencial ni un Workspace elegido por el browser. Un server externo usa HTTPS autenticado con un WorkspaceRuntimeCredential least-privilege guardado solo en server.

El browser nunca recibe MCP tokens, source definitions, credenciales runtime reusables ni credenciales R2.

  1. Deriva Workspace y mode permitido.
  2. Resuelve con @lemn-ltd/brand-runtime y deadline estricto.
  3. Verifica envelope estricto, projectionHash canonico, firma, hashes de definition/compiled/mode, compatibilidad, identidad firmada de Workspace y publicacion, assets publicos y expiracion.
  4. Lee CSS critico, preloads, assets seguros y bootstrap minimo solo desde esa proyeccion verificada.
  5. Emite preloads/style antes del markup y aplica todos los atributos de scope.
  6. Renderiza componentes y solo hidrata cuando projectionHash e identidad del mode coinciden exactamente.
import {
createServiceBindingBrandingRuntimeClient,
resolveBranding,
} from "@lemn-ltd/brand-runtime";
import {
assertBrandingHydrationIdentity,
createBrandingSsrParts,
} from "@lemn-ltd/brand-runtime/server";
const branding = await resolveBranding({
// Es configuracion confiable del host y se contrasta con el response. La
// autoridad RPC viene de los ctx.props estaticos del binding.
workspaceId: WORKSPACE_ID,
modeId: requestedMode,
client: createServiceBindingBrandingRuntimeClient({
binding: env.BRANDING_RUNTIME,
}),
verifier: verifyWithPinnedPublicKey,
embeddedFallback,
allowedAssetOrigins: ["https://assets.ui.le-mn.com"],
timeoutMs: 1500,
});
const ssr = createBrandingSsrParts(branding, { nonce });
assertBrandingHydrationIdentity(
ssr.hydrationIdentity,
JSON.parse(clientBootstrapText),
);

El binding RPC solo expone resolveBranding, exchangeBrandingPreview y resolveBrandingPreview. No es un Fetcher anonimo, no llama las rutas HTTP y no recibe una credencial runtime reusable. Usa createHttpsBrandingRuntimeClient solo desde un server externo y carga su WorkspaceRuntimeCredential desde secrets/configuracion server-side.

Instala versiones exactas:

Terminal window
pnpm add @lemn-ltd/brand-contract@1.0.0 @lemn-ltd/brand-runtime@0.1.1 @lemn-ltd/ui@0.4.0

Cada release incluye un mapa exacto de proyecciones minimas por mode, firmadas independientemente, compatibles y ligadas al Workspace. No incluye el objeto compilado completo privado. Antes de resolver active, runtime verifica todos los modes y exige la misma identidad de publicacion, hashes, default mode y set de modes permitidos. El orden es:

  1. objeto activo actual verificado;
  2. fallback branded embedded verificado;
  3. respuesta unavailable explicitamente branded.

No existe una segunda autoridad ni cache mutable de recovery. Nunca uses source JSON, KV generico, defaults del provider ni tokens fallback del package como recovery de produccion.

Emite solo los preloads de fonts del mode verificado. Agrega a CSP los origins exactos de fonts y assets publicos firmados; exige URLs inmutables, SHA-256/SRI, CORS correcto y caching durable. Los storage keys privados y credenciales nunca son referencias runtime. SSR no evita que el browser descargue una managed font.

Preview esta pinneado a un definition hash exacto y usa handoff one-use, short-lived y ligado a session. La URL limpia y browser storage no contienen secrets reusables. Resolution es no-store; expiry/revoke/mismatch muestran preview unavailable y nunca caen silenciosamente al branding activo.

Prueba active, preview y fallback en ambos modes:

  • contenido branded con JavaScript deshabilitado;
  • CSS, atributos, projection hash e identidad del mode correctos en el primer response;
  • bootstrap y server resolution con identidad igual;
  • cross-Workspace y mode no permitido fallan seguro;
  • proyecciones, hashes canonicos, firmas, identidades o assets tampered se rechazan;
  • el envelope runtime serializado no contiene source definition, storage key, objeto compilado completo ni configuracion de otro mode;
  • timeout usa solo el fallback embedded verificado;
  • preview expirado es explicito; y
  • hydration, a11y, contrast, CSP, fonts y assets pasan.

Registra versiones, commit, comandos, URLs y resultados exactos.