Aiki
Getting Started

Installation

Add the SDK to your project, or host the standalone server and dashboard yourself.

How you install Aiki depends on what you're doing:

  • Building workflows — add the SDK to your TypeScript project and run the server embedded in your app, or point your app at a hosted Aiki server. Follow Add Aiki to your project.
  • Hosting Aiki — run the standalone server and web dashboard as infrastructure, with no SDK install. You need a database: a SQLite file, or a PostgreSQL server. Run the server from the prebuilt aiki binary, or run both from the published Docker images or from source with Bun. Follow Run the standalone server and dashboard.

Add Aiki to your project

You need Node.js 22.16+ or Bun 1.4+, and a database. Aiki ships as ESM only, so your project must be ESM.

Aiki stores its state in one of two databases. You pick one with DATABASE_PROVIDER:

DatabaseDATABASE_PROVIDERWhere it livesAlso set
SQLitesqlitea single file on the Aiki server's diskDATABASE_PATH
PostgreSQL 14+pga Postgres server you runDATABASE_URL

SQLite needs nothing installed, so it's the quickest way to start. A SQLite file serves one server process. Two processes on the same file, such as two servers or two apps that each embed one, can stall each other. To run several server instances, use Postgres.

Install the SDK packages

npm install @aikirun/workflow @aikirun/client @aikirun/worker @aikirun/server

SQLite needs no driver: Aiki uses the SQLite built into Node.js and Bun.

@aikirun/server declares postgres as an optional peer dependency, so your package manager does not install it on its own.

Apply the schema migration

If you will run the standalone server, skip this step — that section covers migration for each way of running it.

DATABASE_PROVIDER=sqlite DATABASE_PATH=./aiki.db \
  npx aiki-server migrate apply

The file is created if it doesn't exist.

On Node 22 and 24, this command and your app print ExperimentalWarning: SQLite is an experimental feature and might change at any time. It is Node's notice about its built-in SQLite.

Re-run this command when upgrading Aiki to apply new migrations.

Run the server

Serve the Aiki server's handler with any HTTP framework, in the same process as your app or in a process dedicated to Aiki. The Quick Start walks through this end to end, including the worker and client.

An embedded server should also be given a timer priority queue. Without one, a run can wait several seconds before it moves on: after it starts, after an event arrives, or when a sleep or retry comes due.

npm install @aikirun/memory
import { inMemoryTimerPriorityQueue } from "@aikirun/memory";

const aikiServer = server({
  db: database({ provider: "sqlite", path: "./aiki.db" }),
  timerPriorityQueue: inMemoryTimerPriorityQueue(),
});

The in-process queue serves one server instance. Across several instances use @aikirun/redis, which hands each timer to exactly one of them — see Server.

Workflow code is identical against an embedded or standalone server, so you can also skip embedding entirely and point your client at a server hosted per Run the standalone server and dashboard.

Want the web dashboard for your embedded server? Serve the server's handler over HTTP, as the Quick Start does, then point the dashboard at it. See Run the dashboard on its own.

Run the standalone server and dashboard

Hosting Aiki as infrastructure means running two components:

  • Server — the Aiki server. Needs DATABASE_PROVIDER, plus DATABASE_PATH for SQLite or DATABASE_URL for Postgres.
  • Dashboard — the web UI, a single-page app that talks to the server.

However you run the server, applications connect the same way:

const aikiClient = client({ url: "http://localhost:9850" });

Before the server starts, apply the schema. This one-off migration step applies the server package's migrations, plus the iam package's when the server runs with auth (AIKI_SERVER_AUTH_SECRET). It needs the same database settings as the server, and is safe to repeat: run it on a fresh database or when an aiki upgrade ships new migrations. Already-applied migrations are skipped.

There are three ways to run them:

  • The aiki binary — runs the server from one downloaded executable with its own runtime, no Node, Bun, or Docker to install. The dashboard is served separately.
  • Docker — run the published images, brought up together by Compose or on their own. Best if you already run containers.
  • From source — clone at a release tag and run with Bun. Best for contributors.

With the aiki binary

Download the binary for your platform from the latest release. It carries the migrate and server commands plus its own runtime. Assets are published for macOS on Apple Silicon (aiki-darwin-arm64) and Linux (aiki-linux-arm64, aiki-linux-x64); on any other platform use Docker or From source instead.

The asset is named for its platform and downloads without the executable bit, so rename it and make it executable before putting it on your PATH:

mv aiki-<platform> aiki
chmod +x aiki

If macOS then refuses to run it, see Troubleshooting.

export DATABASE_PROVIDER=sqlite
export DATABASE_PATH=./aiki.db
aiki migrate apply     # migrates the server package; use --package server,iam to include iam
aiki server start      # serves on 127.0.0.1:9850

Both commands read configuration from the environment; pass --env-file <path> to load it from a file instead.

The server accepts connections from this machine only. To serve other machines, set AIKI_SERVER_HOST=0.0.0.0, and turn on authentication with AIKI_SERVER_AUTH_SECRET and AIKI_SERVER_BASE_URL. Without it, anyone who can reach the server can start and read runs.

The dashboard ships separately — serve it as a static site (see Run the dashboard on its own) or from a container (see With Docker). A dashboard on a different origin than the server needs its origin added to the server's CORS_ORIGINS (via --env-file or the environment).

With Docker

There are three published images: ghcr.io/aikirun/cli — the same aiki binary — runs the migration, ghcr.io/aikirun/server runs the server, and ghcr.io/aikirun/dashboard serves the web UI. Compose wires them together; you can also run them yourself.

With Docker Compose

Download the standalone compose file from the latest release into an empty directory — it pulls that release's published images, so there is nothing to clone or build:

mkdir aiki && cd aiki
curl -fsSL https://github.com/aikirun/aiki/releases/latest/download/docker-compose.yml -o docker-compose.yml

Create a .env next to it that picks the database:

DATABASE_PROVIDER=sqlite

The database file lives on a Docker volume, aiki-data, so it survives restarts and upgrades.

Compose also reads these variables from your shell or from --env-file. Every docker compose command needs DATABASE_PROVIDER, including down and logs, and stops with a message saying so when it's missing. A .env next to the compose file keeps it set for all of them.

docker compose up -d

The migration step applies the packages listed in AIKI_MIGRATE_PACKAGES — by default server, plus iam when AIKI_SERVER_AUTH_SECRET is set. Override the list to depart from that, for example to create the iam tables before turning auth on: AIKI_MIGRATE_PACKAGES=server,iam docker compose up -d.

Without Docker Compose

Run the images yourself when you orchestrate containers with your own tooling. Migrate first, then start the server against the same database:

The file sits on a volume that both containers mount, so it outlives them:

# migration
docker run --rm -v aiki-data:/data -e DATABASE_PROVIDER=sqlite -e DATABASE_PATH=/data/aiki.db \
  ghcr.io/aikirun/cli:<version> migrate apply --package server

# server
docker run -p 9850:9850 -v aiki-data:/data -e DATABASE_PROVIDER=sqlite -e DATABASE_PATH=/data/aiki.db \
  ghcr.io/aikirun/server:<version>

Then start the dashboard, which proxies browser calls to the server:

docker run -p 9851:9851 -e AIKI_SERVER_UPSTREAM_URL=http://your-server:9850 \
  ghcr.io/aikirun/dashboard:<version>

Add --package server,iam to the migration when the server runs with auth. The dashboard image proxies to the server, so it needs no CORS_ORIGINS; to serve the dashboard from a static host instead, see Run the dashboard on its own. Pick versions from the releases.

From source

The server and dashboard also run without Docker (this path needs Bun). Clone at a release tag so you run released code — pick one from the releases:

git clone --branch <version> https://github.com/aikirun/aiki.git
cd aiki
bun install
cp app/server/.env.example app/server/.env
# Edit app/server/.env with your database settings

bun run --cwd app/server db:migrate:apply:server   # the migration step
bun run server                                     # Terminal 1
bun run dashboard                                  # Terminal 2

Run bun run --cwd app/server db:migrate:apply:iam too when the server will run with auth. One piece differs from its container form: the dashboard here is a dev server, and the browser calls the Aiki server directly on localhost:9850 — no proxy and no AIKI_SERVER_UPSTREAM_URL (the .env.example already allows the dashboard's origin through CORS_ORIGINS).

Run the dashboard on its own

Serve the dashboard by itself when the server is embedded in your app (the SDK path above) or when you host the server another way. It is a single-page app that calls the server from the browser. Serve it as a static site; to run it as a container instead, use the dashboard image under With Docker.

The server's URL is baked into the bundle at build time, so the dashboard is built per deployment (the build needs Bun) — from the release tag matching your installed @aikirun/* version:

git clone --branch <version> https://github.com/aikirun/aiki.git
cd aiki
bun install
bun run build:types
VITE_AIKI_SERVER_URL=https://aiki.example.com bun run build:dashboard

Deploy app/dashboard/dist/ to any static file host. Because the browser calls the server cross-origin, two things must line up: the build sets VITE_AIKI_SERVER_URL to the server's URL, and the server's CORS_ORIGINS allows the dashboard's origin. Miss either and the dashboard fails loudly in the browser — its error screen names both.

Environment Variable Reference

These apply to the standalone server and the dashboard. If you embed the server in your own app, you control configuration directly via the server({...}) factory and don't need these.

Server

VariableRequiredDefaultDescription
DATABASE_PROVIDERYes—sqlite for SQLite or pg for Postgres
DATABASE_PATHFor SQLite—Path to the SQLite file; created if it doesn't exist
DATABASE_URLFor Postgres—Postgres connection string
DATABASE_MAX_CONNECTIONSNodriver defaultPostgres connection pool size
DATABASE_CA_CERTNo—PEM contents of a CA certificate used to verify Postgres's TLS certificate. Needed to verify against a private CA (e.g. DigitalOcean, RDS)
AIKI_SERVER_HOSTNo127.0.0.1Address the server listens on. 127.0.0.1 accepts connections from this machine only; 0.0.0.0 accepts them from any machine that can reach it. The Docker images set 0.0.0.0
AIKI_SERVER_PORTNo9850Server port
AIKI_SERVER_BASE_URLIf IAM is on—Public URL of the server; required only when AIKI_SERVER_AUTH_SECRET is also set
AIKI_SERVER_AUTH_SECRETIf IAM is on—Authentication secret; setting this (with AIKI_SERVER_BASE_URL) activates the IAM package
CORS_ORIGINSIf the dashboard is cross-origin—Comma-separated allowed origins, e.g. a static-host dashboard's; unneeded behind the dashboard image's proxy
REDIS_URLNo—Redis connection string, e.g. redis://localhost:6379; enables Redis-backed work distribution and moves timer dispatch from the in-process queue to a shared Redis-backed one
LOG_LEVELNoinfoLog level
PRETTY_LOGSNofalsePretty-print logs; false prints JSON

Dashboard

VariableRequiredDefaultDescription
AIKI_SERVER_UPSTREAM_URLIf using the docker image—Server address the dashboard docker image proxies to; read at container start
AIKI_DASHBOARD_HOSTNo0.0.0.0Address the dashboard docker image listens on. Set 127.0.0.1 when the container runs with --network host, so it accepts connections from this machine only. Keep the default when you publish the port with -p
AIKI_DASHBOARD_PORTNo9851Port for the dashboard
VITE_AIKI_SERVER_URLIf on a static host—Build-time server URL for a dashboard served outside the docker image

Which variable applies depends on how you serve the dashboard: the docker image reads AIKI_SERVER_UPSTREAM_URL (see With Docker), and a static-host build reads VITE_AIKI_SERVER_URL (see Run the dashboard on its own). The bundled docker-compose.yml sets AIKI_SERVER_UPSTREAM_URL for you.

Troubleshooting

macOS won't run the binary. macOS quarantines anything downloaded with a browser. Clear the quarantine, using the name you gave the binary:

xattr -c aiki

Downloads fetched with curl are not quarantined.

The server can't reach your database. The server runs in a container, so localhost in the DATABASE_URL you set names that container — not your machine. When Postgres runs on your machine, use host.docker.internal as the host:

DATABASE_URL=postgresql://user:password@host.docker.internal:5432/aiki

The bundled docker-compose.yml already maps that name to the host. If you run the images yourself, add --add-host=host.docker.internal:host-gateway to each docker run.


Next Steps

On this page