docs

Getting started

inkcap is a single stateful process: a web server and an agent runtime in one. You bring PostgreSQL and an LLM provider; it keeps your conversations running even when no browser is watching.

Requirements

Install and run

git clone https://github.com/jakswa/inkcap
cd inkcap
bun install
cp .env.example .env.local   # set DATABASE_URL and SESSION_SECRET
bun run db:migrate
bun run dev

Open http://localhost:3000 and register — outside production, registration is open by default. Your account is created together with a personal workspace that owns your providers and MCP servers.

Add a provider

Providers live server-side; API keys are stored in the database and never reach the browser. Add one under Providers → Add provider, or seed a llama-server provider from the CLI:

# uses DEV_LLAMA_SERVER / DEV_LLAMA_KEY from your env
bun src/tasks/seed-provider.ts --user you@example.com

Chat on your ChatGPT subscription (Codex)

Providers → Add provider → “Sign in with ChatGPT”. The server runs the Codex CLI’s OAuth flow; OpenAI always redirects back to localhost:1455, which inkcap listens on while a sign-in is pending. Tokens stay server-side and refresh automatically.

Browsing from a different machine than the one running inkcap? After you approve the login, the redirect lands on your browser’s localhost and shows a connection error — that’s expected. Copy the failed http://localhost:1455/auth/callback?… address and paste it into the “Finish a remote ChatGPT sign-in” form on the Add provider page. No tunnel needed: the server holds the pending sign-in state and completes the exchange itself.

Import llama.cpp web UI history

Coming from llama.cpp’s built-in web UI? The importer is idempotent and takes a JSONL export or a zip, including attachments and branch trees:

bun src/tasks/import-llama-ui.ts export.jsonl --user you@example.com

Environment reference

VariableWhat it does
DATABASE_URLPostgreSQL connection string
SESSION_SECRETEncrypts the session cookie; in production it must be ≥32 bytes and not a placeholder
ASSET_VERSIONCache-busting version for static assets; required in production (pass as a Docker build arg)
PORTHTTP port (default 3000)
REGISTRATIONopen or closed; defaults to closed in production, open elsewhere
PUBLIC_ORIGIN, CSRF_TRUSTED_ORIGINS, OUTBOUND_TRUSTED_HOSTSOpt-ins for split-origin deployments (TLS-terminating proxy and/or direct LAN IP access); see .env.example
DEV_LLAMA_SERVER / DEV_LLAMA_KEYFeed the seed-provider task in development

Production (Docker)

The build bundles everything into build/; the image ships no src/ and no node_modules/.

docker build --build-arg ASSET_VERSION=$(git rev-parse --short=7 HEAD) -t inkcap .
docker run -p 3000:3000 --env-file .env.production inkcap

Run migrations inside the image with bun build/tasks/migrate.js. Registration defaults to closed in production — bootstrap your first user from the CLI:

CREATE_USER_PASSWORD=... bun build/tasks/create-user.js --name You --email you@example.com
inkcap is deliberately a single stateful process (web server + runner). Restarts are always safe: boot recovery finalizes any runs that were interrupted mid-generation.

Digging deeper