Guide

Production & deployment.

Aktion is a self-contained web component, so shipping it is mostly “serve a static bundle and mount the element.” The wrinkles are SSR/hydration through the shadow DOM, Content-Security-Policy, and wiring an LLM stream from your edge. This guide covers each.

The whole picture

A production Aktion app has three moving parts: a static bundle (the runtime) served from your host or a CDN, the <aktion-app> element mounted in your page, and — for AI surfaces — an edge function that proxies the LLM and streams tokens back. Everything below is about wiring those three together safely.

Serving the bundle

Run npm run build and serve dist/ from any static host — every artifact in dist/ is self-contained, including the CSS (it is injected into each instance’s shadow root, so the host page needs no extra stylesheet). Or load it from the CDN so it is cached across pages and apps:

<script type="module" src="https://cdn.example.com/aktion@0.5/dist/aktion.js"></script>
<aktion-app theme="dark"></aktion-app>

Pin a version (@0.5.5) for reproducible builds and set a long Cache-Control max-age on the immutable bundle.

Because the runtime is a plain static ES module, any static host works — Netlify, Vercel, Cloudflare Pages, GitHub Pages, S3 + CloudFront, or your own nginx. There is no server runtime to provision for the UI itself; the only server-side piece you may need is the edge function that proxies your model (see below).

SSR & hydration

For server-side rendering and static generation, import renderToString(program, { path, initialState }) — it returns { html, state } so you can embed crawlable markup in your page shell and serialise state for client hydration. renderToStaticMarkup(program, opts) emits markup only, for fully static pages. The renderer needs a DOM, so in Node register happy-dom / jsdom on globalThis first.

import { renderToString } from "aktion-runtime";

const { html, state } = renderToString(program, {
  path: "/dashboard",            // seeds the router for the requested route
  initialState: { user: me },    // pre-populate reactive atoms
});
// Embed `html` in your shell and inline `state` for the client to hydrate.

On the client you can also persist and resume reactive state so a reload (or the server-rendered shell) restores the app without re-running the LLM:

MethodUse
serializeState()Returns every reactive atom as a plain JSON-friendly object — persist it (cookie, KV, localStorage) for SSR / resumption.
hydrateState(snapshot)Applies a snapshot to the live store and schedules a re-render. Atoms not in the snapshot are untouched.
// On unload / checkpoint:
const snapshot = app.serializeState();
await fetch("/session", { method: "PUT", body: JSON.stringify(snapshot) });

// On next load, after setting the program text:
app.setResponse(savedProgram);
app.hydrateState(await loadSnapshot());

Content-Security-Policy

The bundle does not use eval. However, effect and action bodies are evaluated with new Function(...) so that the DSL can run arbitrary JS expressions — that requires 'unsafe-eval' in your script-src.

If you cannot relax CSP, you can still run Aktion: avoid emitting complex JS expressions from the LLM. Declarative constructs — component trees, $http(), and the $util helper namespace — keep working without 'unsafe-eval'.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-eval' https://cdn.example.com;
  connect-src 'self' https://api.your-llm.example.com;

Integrity & pinning

Edge-function LLM streaming

The common production shape is: the browser holds the <aktion-app>, an edge function proxies the LLM (keeping your API key server-side), and tokens are streamed back and fed to appendChunk:

const res = await fetch("/api/generate", { method: "POST", body: prompt });
const reader = res.body.getReader();
const decoder = new TextDecoder();
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  app.appendChunk(decoder.decode(value, { stream: true }));
}

See the LLM integration guide for provider-specific stream parsing and prompt selection.

Error reporting & telemetry

Pre-launch checklist

Next