Akan.js v3
Akan.js v3
Release note · Sep 25, 2026
v2 gave Akan one runtime. v3 gives every app a second kind of user. The guards that protect your screens now also publish them to AI agents, as MCP tools, as prompts, and as an assistant working inside the page, and the UI layer underneath has been rebuilt on native tokens and recipes — and it is faster than v2 on every number we measured. Here is what changed, in the order it matters when you build.
01
POST /mcp is mounted by default. Every endpoint its guards admit becomes a tool that Claude, Cursor or any MCP client can call, with the same field masking and the same guards as your own screens. Tool descriptions come from the dictionary you already write. An app that mounts libs/shared also serves an OAuth 2.1 authorization server, so an agent signs in as the user and acts with exactly that user's rights.
There is no opt-in list to keep up to date. The guards already are the authorization decision: mcp: false keeps a guarded endpoint off the agent shelf, and the Person guard keeps an act that only a person should perform away from models.
02
Mount <Agent.Chat /> once in a layout and your users get an assistant that reads the rendered screen and operates it, inside their own browser session, through the same controls they use. A tool is declared beside the control it drives: st.tool(...) returns the handler you pass to onClick, so the person and the agent share one code path. A form publishes itself when its setter is passed by reference.
It reads the pictures and documents a user attaches, listens and speaks, batches tool calls, and asks for approval before anything that changes data. OpenAI-compatible hosts and Anthropic both work, configured in option.ts, and the relay refuses every call until the app names a guard.
03
page().prompt(name, description) publishes a screen as an MCP prompt. When an agent runs it, the page's own fetches execute under the caller's token and nothing is rendered; the agent receives that data as resources, plus the tools of the modules the page read from. A screen you built for people becomes a briefing for an agent, with no second implementation to keep in sync.
04
daisyUI is gone. A theme is now a set of semantic token values in page/styles.css (background, foreground, primary, muted and their -foreground pairs), and every look is a server-safe recipe built on tailwind-variants: buttonRecipe, badgeRecipe, inputRecipe, and your own under ui/Recipe/. Lint closes the vocabulary, so a raw palette class or a hex value fails the build instead of shipping a color that ignores the theme.
Each app's recipes are indexed into its AGENTS.md, so an agent reuses a look instead of inventing a near-duplicate.
05
A page/**/_overrides.tsx manifest swaps akanjs/ui components (Modal, Button, Table, Input, the agent chat's parts, even recipe slots) for your own, for that route subtree only. Manifests nest like layouts and merge slot by slot, closest wins, and they now reach root layouts and portalled overlays. It first appeared in 2.3.11; v3 is where it covers the whole UI. A redesign no longer means a fork.
06
A route file exports one chain: page(), layout() or rootLayout(), with every setting as a stage (.param(), .search(), .config(), .head(), .prompt(), .render()). Arguments arrive typed: an ID is a string, an Int a number, a Date a Dayjs, and a path value the type refuses answers not-found. The old default export beside pageConfig still loads, with a deprecation warning.
Faster and smaller
Agents, MCP and a new UI system could easily have cost speed, so we ran the same benchmark as for v2. v3 is faster, uses less memory, and starts twice as fast.
| Metric | v2 | v3 | Change |
|---|---|---|---|
| Requests per second | 112K | 123K | +10% |
| Response time (p99) | 1.30 ms | 1.04 ms | −20% |
| Startup time | 204 ms | 102 ms | −50% |
| Memory at rest | 84 MB | 57 MB | −32% |
| Memory under load | 105 MB | 85 MB | −19% |
Your app is ready to take requests in about half the time it took on v2. Deploys, restarts and scale-outs come back online sooner.
The same app now uses about a third less memory when idle, so more of it fits on the same machine.
Throughput went up and slow responses got faster, keeping Akan.js alongside the fastest Bun frameworks.
Akan.js does far more than a plain router — database, auth, server rendering and agents come built in — and it still keeps pace with the lightweight Bun frameworks.
Requests per second
| Framework | Startup | Memory at rest |
|---|---|---|
| raw Bun.serve(Bun) | 102.0 ms | 31.5 MB |
| ElysiaJS(Bun) | 104.0 ms | 37.0 MB |
| Hono(Bun) | 103.4 ms | 33.1 MB |
| Akan.js v3(Bun) | 102.2 ms | 57.2 MB |
| raw sqlite(Bun) | 101.9 ms | 35.8 MB |
| Fastify(Node) | 103.7 ms | 69.3 MB |
Measured on an Apple M4 Pro MacBook Pro with production builds, 50 concurrent users. Fastify runs on Node, so its number partly reflects the runtime. Raw data and the benchmark harness live in benchmarks/api-benchmark.
26MB → 8.1MB
Client build output, 605 chunks down to 258
3.5ms → 0.9ms
Hydrating 1,000 rows on the client
−33%
Time for a 50-row list query, with 85% fewer allocations
103KB → 13KB
This site's CSS on the wire, precompressed with brotli
86MB → 6.2MB
This site's container image built as API-only (web: false)
v3 adds more than any release before it and is still faster than v2 on every number we measured. Upgrade and your app gets quicker, lighter and faster to start — the speed comes with the framework, not with your code.
07
fetch.init*, view* and edit* split into one promise per field, so each section streams behind its own boundary. akan quality ssr measures how much of each app renders on the server and flags a “use client” nothing needs.
08
Give a field a text role and write q.search(text) in a filter. SQLite FTS5 with bm25 ordering, and a role on a secret field is a compile error.
09
.live() on a slice pushes entered, updated and left events to every list showing it, and Load.Units applies them. No polling, and the room keeps the slice's guards.
10
Edit and create shells save the form per user and per record and offer it back after an accidental close, a route change or a killed app. Secret fields are never saved.
11
cascade: removeRef and removeWith declare which way a removal travels, and every filter also generates query-level remove and update for models with no removal side effect.
12
An endpoint's { timeout } bounds both ends of a call, { cache } caches argument-free queries after the guards, pubsub(Binary) sends raw websocket frames, and API and websocket prefixes are configurable.
13
One replica runs in-process with no gateway, web: false or { csr: false } drops surfaces an app does not serve, and the generated image carries only what the app declares.
14
Every log line is a structured record with a trace id. akan logs tails and filters a running server; ndjson stdout, an SSE stream, one canonical line per call and a flight recorder are each one env var away.
15
akan start a,b boots several apps in one full-screen view with a log file per app, and akan tunnel or --share puts a local app on a public URL.
16
Every workspace carries a generated AGENTS.md, bundled guidelines, a plan-then-apply workflow MCP, and akan code, a terminal coding agent that works through them.
17
Mobile push notifications and deep links are declared in the plugins list of akan.config.ts instead of living inside the framework.
18
New rules catch client/server import leaks, fetch.init* in client files, model-typed props on client components and raw Error throws before they ship.
These are the changes that need a hand. akan lint and akan typecheck point at most of them.
daisyUI classes and raw palette classes render unstyled. Move to the semantic tokens; the lint page maps each old class to its replacement.
/conventions/workspace/lintRoute files move to page()…render(). .metadata() and .gaTrackingId() are removed; the head is JSX in .head().
/docs/core/routing/mcp is on by default: every guard declares static scope, every custom endpoint names its guards, and JWT_SECRET is required outside local. AKAN_MCP=false turns the surface off.
/cheatsheet/interface/mcpdeleteMany is now removeMany, and countDocuments is deprecated in favour of count.
/docs/core/data-layerRetry and @CacheMethod are removed in favour of an endpoint's { timeout } and { cache } and memory(); model loaders cache only when they declare { cache }.
/cheatsheet/performance/cachingA model's Date fields are prototype accessors: copy a model with new cnst.X().set(model), never a spread.
/conventions/module/constantThe generated image installs only ca-certificates and tzdata. Declare ffmpeg or Chromium in docker.preRuns.
/cheatsheet/dev/dockerlogger.log() is now .info(), usePushNotification moved to @libs/util/webkit, and the --ai CLI commands are replaced by akan code.
/references/cli/codev3 is the stage where agents joined the full stack. What comes next is the agent network across sessions, people and apps, Akan Cloud for deployment, context-side rendering as the agentic rendering step after SSR and CSR, and a repository engine that changes and extends the app itself.
See the roadmap