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.
Introduction▾
Tutorials▾
Core Concepts▾
Architecture▾

Akan Runtime

Akan applications run on a Bun-based runtime that connects app code, generated artifacts, server routes, and pages. The app entry point (main.ts) starts the runtime, and Akan handles the server shape behind it.
apps/myapp/main.ts
When Akan App starts, Akan Server prepares everything the app can serve. In practice, the runtime exposes four kinds of work.
  • Internal API (Queue, Timer, etc.): internal work that runs without a browser request.
  • API (HTTP, WebSocket): public communication for data requests and realtime updates.
  • SSR Pages (Web): web pages rendered by the server and sent to the browser.
  • CSR Page (Android, iOS): client-rendered pages used by native targets.
Runtime overview
App code runs in the Akan App, which runs the Akan Server, and the server exposes an internal API for queues and timers, an HTTP and WebSocket API, SSR pages for the web and CSR pages for Android and iOS.
AKAN_REPLICA decides how many server processes each role gets, and it defaults to 0,0,1 everywhere: one all server and nothing else. With a single traffic replica there is nothing to balance, so Akan App runs that server inside its own process instead of spawning it and proxying to it. The container holds one process, and every request skips a proxy hop.
  • all: runs both federation and batch behavior in one server process. This is the default, and the shape almost every deployment ships.
  • federation: serves browser traffic such as pages, API calls, and WebSocket connections.
  • batch: runs background work such as queues, timers, and scheduled jobs.
Default: one process, no gateway
The browser reaches one process in the container where Akan App and Akan Server run together, serving pages, API and WebSocket and running queues, timers and jobs; a separate RSC worker process exists only for web.
Five things bring the gateway back: two or more replicas, a batch-only replica that never listens, AKAN_SOLO=false, passing replica to new AkanApp(...), and akan start. Then Akan App spawns the servers and load-balances browser traffic across the ready federation and all processes.
Replica and server modes
The browser reaches the Akan App acting as gateway and load balancer inside the container; it spreads traffic over three federation servers for pages, API and WebSocket, and runs a batch server for queues, timers and jobs.

Identity And Environment

The root .env file decides which organization, domain, environment, operation mode, and log level the app uses while it runs. Most projects keep these values stable, but changing them lets the same app behave like a local, debug, develop, or production-like service.
.env
Four of those names answer who this app is and where it runs, and the first two are required:
AKAN_PUBLIC_REPO_NAMEstringrequired
Organization or repository namespace, usually fixed for the life of the project.
AKAN_PUBLIC_SERVE_DOMAINstringrequired
The domain the app builds links, callbacks, and domain-based routes from.
AKAN_PUBLIC_ENVlocal | debug | develop | main | testingdefault debug
Which data set the app runs against, from local test data up to production-like main.
AKAN_PUBLIC_OPERATION_MODElocal | edge | cloud | moduledefault local when ENV=local, else cloud
Where clients connect: local runtime, cloud, or edge paths; module is only in the type.
In practice you move two of them together. Build a feature with ENV=local and OPERATION_MODE=local, switch ENV to debug or develop when you need shared data or shared services, and deploy with ENV=main against whichever operation mode the cluster serves:
.env
Two more narrow who reaches the server, for one only its own computer calls, such as the server a desktop app carries:
AKAN_LISTEN_HOSTstringdefault every interface
The one address the server binds, such as 127.0.0.1.
AKAN_ALLOWED_HOSTShost:port, …
The Host headers it answers; any other request, socket upgrade and preflight included, gets 403.

Database Variables

Which database mode a deployment runs and where its data lives are the deployment's to say. These variables win over the same values in env.server.ts, so one image can serve several deployments:
AKAN_DATABASE_MODEsingle | multiple | clusterdefault the first declared mode
One of database.modes; a deployment of a build that declares several must set it.
AKAN_SQLITE_DIRstringdefault /workspace/sqlite in the image
The folder for any SQLite file no path names: the database, and single's cache and queue file.
SQLITE_DATABASE_PATHstring
Moves the database file alone, in single and multiple; it wins over AKAN_SQLITE_DIR.
AKAN_SOLID_DB_PATHstring
The SQLite file where single keeps its cache, queue and pubsub.
POSTGRES_URLstring
The cluster database (alias POSTGRES_URI), with pool size and SSL in its query string.
POSTGRES_HOSTstringdefault localhost
The URL in parts, with POSTGRES_PORT, POSTGRES_DATABASE, POSTGRES_USER, POSTGRES_PASSWORD.
POSTGRES_INSIGHT_URLstring
Logs the SQL console in on cluster, as a role that may read base columns only.
REDIS_URIstringrequired outside local
The one Redis every instance of multiple or cluster shares; rediss:// turns on TLS.
AKAN_STORAGE_SHAREDtrue | 1
Every instance mounts one upload volume; disk uploads in multiple and cluster need it.
An app that declares database: { modes: ["single", "cluster"] } ships one image that serves both of these:
.env
  • Only local development may skip REDIS_URI. A developer machine falls back to localhost, or to REDIS_HOST when akan start runs against a shared environment.
  • Behind a PgBouncer in transaction mode, add prepare=false to POSTGRES_URL; the driver reads every such setting from the query string.

Logging Variables

The level ladder is trace, verbose, debug, info, warn, error, and three destinations read it independently: the container's stdout, the rotating log file, and any sink the app registered. Everything else here decides how much structure travels with a record and who is allowed to ask for more.
AKAN_PUBLIC_LOG_LEVELtrace | verbose | debug | info | warn | errordefault info
How much runtime output the console carries; the deprecated log means info.
AKAN_LOG_STDOUT_LEVELtrace | verbose | debug | info | warn | errordefault AKAN_PUBLIC_LOG_LEVEL
What goes to the container's stdout, in either format; info is the production pick.
AKAN_LOG_FILE_LEVELtrace | verbose | debug | info | warn | errordefault trace
How much structured Logger output goes to files, independent of the console level.
AKAN_LOG_FORMATtext | ndjson | ndjson-onlydefault text
text for people; ndjson makes stdout one JSON record per line, ndjson-only the file too.
AKAN_LOG_TO_FILE0 | 1default 1
Writes gateway and child logs to runtime/logs; off in the production image.
AKAN_LOG_DIRstringdefault runtime/logs
Where file logging writes, when the default directory is not where the volume is mounted.
AKAN_LOG_MAX_SIZE_MBnumberdefault 50
Create the next sequence file when a process log reaches this size.
AKAN_LOG_MAX_FILESnumberdefault 100
Keep this many rotated files per process key, such as gateway or child-0.
AKAN_LOG_CONTEXT0 | 1default 1
Tags each call's records with traceId, endpoint and origin; independent of AKAN_TRACE.
AKAN_LOG_STREAM0 | 1default 0
1 forwards child records to the gateway always, not only while akan logs or .tail listens.
AKAN_LOG_STREAM_TOKENstringdefault unset — route absent
Mounts GET /_akan/app/logs, an SSE stream of the ring buffer, for a matching bearer token.
AKAN_LOG_CANONICAL0 | 1 | all | slowdefault 0
One record per call at its end; 1 or all logs every call, slow only failed or slow ones.
AKAN_LOG_FLIGHT0 | 1default 0
Buffers each call's last 64 sub-level records; promotes them if it failed or ran slow.
AKAN_LOG_FLIGHT_MSnumberdefault 1000
A call at least this long is slow, for the flight recorder and the slow canonical mode.
AKAN_LOG_FLIGHT_MAXnumberdefault 65536
Caps the records the process holds at once; a call past the cap runs unrecorded.
AKAN_LOG_BUFFERnumberdefault 2000
How many records the in-memory hub keeps for akan logs and the SSE stream to replay.
AKAN_LOG_BUFFER_MBnumberdefault 4
The same buffer's byte ceiling; whichever limit is reached first applies.
AKAN_LOG_DEBUG_HEADERstringdefault unset — local only
The secret x-akan-debug must carry outside local to log that one request at trace.
The ring the gateway (or the solo replica) keeps for akan logs --replay and .trace holds AKAN_LOG_BUFFER records or AKAN_LOG_BUFFER_MB, 2,000 or 4 MB by default, whichever fills first, and the older record goes first.

getEnv()

getEnv() is the runtime helper that turns .env values into the information your app actually uses. Instead of hand-writing API URLs or WebSocket URLs, app code can read the prepared values from getEnv().
Using getEnv()
Local mode
When OPERATION_MODE is local, getEnv() points the browser and API client to your local Akan runtime, usually localhost:8282.
local
Cloud / edge mode
When OPERATION_MODE is cloud or edge, getEnv() builds service URLs from the app name, environment, and serve domain.
cloud / edge

OpenAPI JSON

Akan can expose the HTTP query and mutation surface declared in signal files as an OpenAPI 3.1 document. This is useful when you want to connect Swagger, Redoc, external clients, or SDK generation tools to the same API shape Akan already uses.
apps/myapp/main.ts
After enabling it, request /openapi.json from the app origin. In local mode, the document is usually available at localhost:8282/openapi.json. The normal API prefix stays at /api; OpenAPI JSON is served as a framework metadata route.
Read the OpenAPI document
App option
Use this when the app should always expose OpenAPI JSON in that entry point.
new AkanApp("./server", { openapi: true })
Environment variable
Use this when deployment or local scripts should decide whether the endpoint is available.
AKAN_OPENAPI=true
Server option
Use this when you start AkanServer directly instead of going through AkanApp.
new AkanServer("myapp", env, "all", lib, { openapi: true })

Selective Module Boot

An app mounts every module its libraries declare. The modules option narrows that: name the modules a process should serve and Akan boots those plus the ones they depend on, leaving the rest out of the container entirely. A module left out has no service, no signal, no route, and no scheduled job. This is how one codebase runs as several small processes, such as a batch worker that only needs its own domain.
apps/myapp/main.ts
Dependencies are followed for you, so you list entry points instead of the whole graph. A named module pulls in every service and signal it injects, and every model its cascade removes.
disableModules and disableLibs work from the other end: mount everything except what you name and whatever reaches it. disableLibs takes a library's name and stands for every module it registers. Both are accepted wherever modules is, and as AKAN_DISABLE_MODULES and AKAN_DISABLE_LIBS in the environment. A module named in both modules and an exclusion is left out.
apps/myapp/main.ts
App option
Use this when the entry point itself decides which modules the process serves. Every replica it spawns gets the same selection.
new AkanApp("./server", { modules: ["article"] })
Environment variable
Use this when deployment decides the split, so one image can run as different processes without a second entry point.
AKAN_MODULES=article,file
Server option
Use this when you start AkanServer directly instead of going through AkanApp.
new AkanServer("myapp", env, "all", lib, { modules: ["article"] })

Health, Metrics, Logs

Akan runtime exposes simple ways to check whether the app is alive, how busy it is, and what it is doing. In local development, these are mostly useful when a page does not load or a background job seems stuck.
Health
Use this to check whether the server processes are running and ready. A solo replica answers it itself, in the same shape the gateway uses, so a probe reads one contract either way.
health
Metrics
Use this to see runtime counts such as active requests, WebSocket connections, rooms, and process metrics.
metrics
Logs
Use AKAN_PUBLIC_LOG_LEVEL to choose how much detail appears in the terminal. AkanApp also stores gateway and child process output in runtime/logs by default, using AKAN_LOG_FILE_LEVEL for structured Logger output and rotating files by date and size.
logs
File names include app name, environment, operation mode, local date, process key, and sequence. Direct console.log calls from child servers are captured through stdout/stderr pipes; direct gateway console.log calls are not part of Logger sink capture.
Runtime checks
Developer
Akan App(gateway or solo)
/_akan/app/health
/_akan/app/metrics
Terminal Logs
Running / Ready
Requests, Sockets, Memory
Debug Details

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