YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.
CLI Reference▾
AkanJS Reference▾
UI Reference▾

akanjs/server

akanjs/server is the server half of Akan: it starts the app, holds the server settings, and sees page loads before the router. Import it only from server files — main.ts, lib/option.ts and srvkit/.
Words Used On This Page
gateway
The front process: it starts the replicas and relays HTTP and WebSocket traffic to them.
replica
One server process that runs your modules. It takes traffic, runs batch work, or both.
solo
A single replica running inside the main.ts process itself, with no gateway in front.
RSC worker
A separate process that renders pages on the server. Each replica serving pages has one.
web proxy
A class that sees each page load before the router and can redirect, rewrite or answer it.
Exports
Starts the app from main.ts: one process, or a gateway with replicas.
What new AkanApp() takes: replicas, port, route prefixes and which modules boot.
AkanServer
One replica's server, generated into server.ts. It decides which web surfaces are served.
AkanLib
Bundles one app's or lib's modules and option. Also generated into server.ts.
The builder lib/option.ts exports: injected values, middleware, proxies, MCP and LLM settings.
Helpers a web proxy returns to continue, rewrite or redirect a request.
The interface a web proxy class implements: one use(request) method.
The two built-in proxies. Every app runs them before its own.
Legacy decorator: logs a warning and returns undefined instead of throwing.
Legacy decorator: runs a method in a database transaction.
Everything else
Build-artifact types and console, OAuth, sitemap and metrics helpers the framework and CLI use.
Where It Is Imported
main.ts
Use the leaf path, which keeps the SSR renderer out of the gateway.
server.ts
Generated; never edit it by hand.
lib/option.ts
One per app and one per lib.
srvkit/*.ts
Server-only helpers: proxy classes and legacy decorated classes.

AkanApp

AkanApp is what main.ts starts. It decides how many server processes run, and keeps them running until the container stops.
A whole main.ts:
apps/myapp/main.ts
  • Two call shapes. new AkanApp(serverPath?, options?) or new AkanApp(options). The server path defaults to ./server, next to main.ts.
  • start() boots everything. In solo mode it loads server.ts in the same process; otherwise it spawns one child per replica.
One Process Or A Gateway
With one replica that takes traffic there is nothing to balance, so AkanApp runs it inside its own process. Anything else puts a gateway in front:
Setting
Solo
Gateway
Default
AKAN_REPLICA=0,0,1
✓
One replica that takes traffic and runs batch work. There is nothing to balance.
Brings the gateway back
AKAN_REPLICA=0,0,2
✓
Two or more replicas: the gateway spreads traffic across them.
AKAN_REPLICA=0,1,0
✓
A batch-only replica never listens, so the gateway answers health checks.
new AkanApp({ replica })
✓
Stating a replica layout in code asks for the gateway that serves it.
AKAN_SOLO=false
✓
Forces the gateway even for one replica.
akan start
✓
The dev server always runs the gateway.
✓runs this waydoes not
What the gateway does:
Starts Replicas
Spawns each replica and restarts one that crashes, waiting 1s, 2s, 4s… up to 30s.
Relays Traffic
Forwards HTTP over a unix socket and WebSocket over a local port to each replica.
Reports Health
Collects each replica's metrics and answers for the whole tree.
/_akan/app/health · /_akan/app/metrics
Stops Cleanly
On SIGINT or SIGTERM it asks each replica to stop, then kills what is left after 30s.
  • Solo answers the same routes. A solo process serves /_akan/app/health and /_akan/app/metrics in the gateway's shape, so a probe reads one contract either way.
  • Nothing restarts a solo process but your orchestrator. Keep liveness and readiness probes on the container.

AkanAppOptions

Every field is optional. With none, AkanApp runs one replica on port 8282, and each field can also come from the env named beside it; the option wins.
replicanumber | stringdefault "0,0,1"AKAN_REPLICA
Replica counts as federation,batch,all. Passing it here keeps the gateway on.
serverPathstringdefault "./server"
The server module each replica runs, resolved next to main.ts.
runtimeDirstringdefault local/apps/<app>/runtimeAKAN_RUNTIME_DIR
Replica sockets and rotating logs. It is ./runtime when NODE_ENV=production.
portnumberdefault 8282PORT
The port the app listens on. A replica calling itself uses it too.
wsBasePortnumberdefault port + 10000AKAN_WS_BASE_PORT
Replica i takes WebSocket traffic from the gateway on this port plus i.
openapibooleandefault falseAKAN_OPENAPI
Serves /openapi.json, a description of every endpoint.
prefixstringdefault "/api"AKAN_API_PREFIX
Where endpoints are mounted. CSR and mobile bundles follow api.prefix in akan.config.ts.
websocketPrefixstringdefault "/ws"AKAN_WS_PREFIX
Where the WebSocket upgrade sits, under prefix.
modulesstring[]AKAN_MODULES
Boot only these modules and the ones they reach. Empty boots every enabled module.
disableModulesstring[]AKAN_DISABLE_MODULES
Boot everything except these and whatever reaches them. Applied after modules.
disableLibsstring[]AKAN_DISABLE_LIBS
Leave out every module the named libs registered, and whatever reaches them.
solobooleanAKAN_SOLO
Overrides the automatic solo or gateway choice. The env can turn solo off, never on.
Reading replica
replica is three counts separated by commas, one per role:
1federation
Takes traffic. Skips work declared serverMode: "batch".
2batch
Never listens. Skips work declared serverMode: "federation".
3all
Takes traffic and runs every kind of work.
Three replicas behind a gateway, all booting only the article module:
apps/myapp/main.ts
  • 1,0,2 is three processes. One federation replica and two all replicas, with the gateway in front.
  • A bare number means federation. replica: 3 is 3,0,0, so work declared serverMode: "batch" runs nowhere.
  • Modules bring their dependencies. modules: ["article"] also boots every service and signal that article injects, in every replica.

AkanServer Web Surfaces

Besides its API, an app serves up to two web surfaces: SSR pages, and the CSR bundle the mobile app ships. The build decides which exist; at runtime you can only turn them off.
Setting
API
SSR
CSR
At build — akan.config.ts
web: true
✓
✓
✓
The default: pages, the mobile bundle and the API.
web: { csr: false }
✓
✓
No mobile bundle, so /__csr and ?csr=true are gone. Not allowed with a native section.
web: false
✓
An API-only build. Nothing under page/ is served.
At runtime — env
AKAN_CSR=false
✓
✓
Drops the CSR bundle for this deployment.
AKAN_SSR=false
✓
Drops pages and the RSC worker. CSR goes too, since its bundle reuses the SSR stylesheet.
✓servednot served
To turn a surface off for one deployment, set the env:
Terminal
  • Runtime only narrows. false or 0 turns a surface off, and a surface the build left out never comes back.
  • The same from code. server.setWeb(true | false | { csr }) or server.init({ web }) narrows the same way, before the server starts.
  • Why turn SSR off. SSR is the RSC renderer plus its own RSC worker process per replica; an API-only process runs neither.
  • Dev ignores it. akan start serves every surface, whatever web says.

AkanOption

lib/option.ts exports one AkanOption. It carries the server settings a lib or app owns, from injected values to MCP and LLM settings.
use(fn | object)
Registers values a service reads with use<T>(). A function gets the env; a Promise is awaited.
applyMiddleware(...classes)
Adds signal middleware. Logging and Timeout are already registered.
applyAdaptor(role, adaptor)
Swaps a built-in adaptor role, such as LlmAdaptorRole, for your own class.
applyWebProxy(...proxies)
Adds web proxies, each as a class or { proxy, matcher }.
setMcp(option | fn)
Settings for the MCP server at /mcp. false takes it off.
setAgentAccess(guards)
Guards a caller must pass to spend the LLM key through the agent chat. Several are ANDed.
setLlm(option | fn)
The model the agent relay talks to: apiKey, model, host and more.
setCrossSite(option)
Extra origins a browser may send mutations and open the websocket from. { enabled: false } turns the check off.
A typical app option:
apps/myapp/lib/option.ts
  • The type parameter is the env. AkanOption<ModulesOptions> types what every function form receives, so keys and secrets come from the app's server env.
  • use is for constructor-style clients. An adapt() adaptor registers itself, so never list one here.
When Several Libs Set It
Every lib's option is read in mount order, and the app's comes last:
use
Keys must be unique across all libs. llmOption is reserved.
applyMiddleware
One per refName; the later registration wins.
applyAdaptor
The last override of a role wins.
applyWebProxy
All run: the two built-ins first, then each lib's in order.
setMcp
Merged field by field; the app's values win.
setLlm
Merged field by field, so a lib may name the host and the app the key.
setAgentAccess
The last call wins; null clears what a lib set.
setCrossSite
The last call wins.

AkanResponse

AkanResponse builds what a web proxy's use() returns. Each helper says whether the request goes on, moves to another URL, or ends here.
Helper
↳ What happens
AkanResponse.next({ request: { headers } })
Goes on to the next proxy and the page, carrying the headers you set.
AkanResponse.rewrite(url, { request? })
Goes on with a new URL. The browser's address bar keeps the old one.
AkanResponse.redirect(url, status = 307)
Returns a redirect Response. Later proxies and the page do not run.
A proxy that uses all three:
apps/myapp/srvkit/docsRoutingProxy.ts
  • Headers you pass replace the originals. Start from new Headers(request.headers); pass none and the originals go on unchanged.
  • A rewrite keeps the request. The method, the body and the route params carry over; only the URL changes.
  • redirect is a plain Response. Returning any Response ends the chain the same way.
  • A client-side navigation gets none of it. A <Link> to /en/help renders /en/help itself, neither redirected nor rewritten, so link to the page the proxy would have chosen.

WebProxy

A WebProxy is a class with one use(request) method. It sees every page load before the router, so it suits redirects, host-based routing and headers a page reads. A client-side navigation, a <Link> click or router.push, is not a page load and never reaches it.
A proxy that closes the shop pages during maintenance:
apps/myapp/srvkit/maintenanceProxy.ts
Register it in lib/option.ts, narrowed to the shop pages with a matcher:
apps/myapp/lib/option.ts
  • Page loads only. Endpoints under the API prefix, the WebSocket, /_akan/* and client-side navigations, which load from /__rsc, never reach a proxy. A navigation still gets the built-in locale and basePath handling.
  • Built-ins run first. LocaleWebProxy and HostBasePathWebProxy run before yours, so the path you see already starts with a locale.
  • Each proxy sees the previous one's request. Proxies run in registration order, and the headers or URL one sets are what the next one reads.
  • static refName is required. It names the proxy, and the class type demands it.
What use() Returns
undefined
Passes the request on unchanged.
Response
Answers right away. Later proxies and the page do not run.
AkanResponse.nextAkanResponse.rewrite
Goes on with new headers or a new URL, as the AkanResponse section shows.
Matchers
(omitted)
Page paths only: skips /__csr, /_akan/* and paths with a file extension.
"/ko/shop"
That path and everything under it.
/^\/[a-z]{2}\/shop/
A RegExp, tested against the pathname.
(request) => boolean
Your own test on the whole request.
Built-In Proxies
LocaleWebProxy
Redirects a path with no locale to /<locale>/… (307) and sets x-locale and x-path.
HostBasePathWebProxy
Maps the host to a basePath from routes in akan.config.ts and rewrites into it.
Types
WebProxy
The interface. use(request) returns a WebProxyReturn, sync or async.
WebProxyCls
A proxy class: constructed with no arguments, with a static refName.
WebProxyRegistration
What applyWebProxy takes: a class, or { proxy, matcher? }.
WebProxyMatcher
A path prefix string, a RegExp, or (request) => boolean.
WebProxyReturn
Response, a WebProxyResult, or undefined.
WebProxyResultWebProxyNextInit
What next and rewrite return, and the { request: { headers } } they take.

Try

@Try() is a legacy method decorator for best-effort external calls: when the method throws, it logs a warning and returns undefined instead. The storage adaptors in libs/util still use it.
The legacy shape, on a constructor-style client:
apps/myapp/srvkit/partnerApi.ts
  • It logs through this.logger. On a class without a logger field, the error disappears silently.
  • The caller gets undefined. Check the result before you use it.
  • The method becomes async. The wrapper always returns a Promise, even around a synchronous method.
New code catches the error itself in an adapt() adaptor, as catch → logger.error → return null:
apps/myapp/srvkit/partnerApi.ts

Transaction

@Transaction() is one more legacy method decorator from the same file, for server-side services. It is all or nothing: it commits when the method returns and rolls back when it throws.
On a database service, two writes that must land together:
apps/myapp/lib/wallet/wallet.service.ts
  • Nested calls join. A transactional method called from inside another runs in the outer transaction.
  • It needs a database. It finds one on a model or a database service; on any other class it throws.
  • Caching is not a decorator. For a remembered answer, declare { cache: <ms> } on a query endpoint, or keep the value in a memory(...) field.

Released under the MIT License

Connect your AI to these docs

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js All rights reserved.System managed bybassman