Last updated · August 29, 2026

Install and configure your AI agents

Everything the app ships with, in one place: what it takes to install it, how to start it on your machine, and where to configure each piece once it's running.

Who this guide is for

There are two ways to run oruka and this guide covers only one. On Pro and Managed we host and operate the instance: you install no Node, Docker, PostgreSQL, sandbox or OpenTelemetry — you sign in and you're done. The database is ours and there is a single one behind every account: you don't choose Postgres and you never see Settings → Database, because none of it is yours to configure. The only thing you supply is credentials for your own integrations — WhatsApp, Stripe, Shopify and the rest. On Enterprise you run everything on your own infrastructure under a perpetual licence: the database is yours (local container or hosted, your call), the keys are yours, and so is the operating. What follows is that second path.

Requirements

Node.js 24, Corepack with pnpm 10.33.2, Docker Engine or Docker Desktop, and an OpenAI or Anthropic API key with available quota.

Quick install (all platforms)

A single script installs everything on macOS, Linux, or Windows: Git, Docker, Node.js 24, pnpm, project dependencies, PostgreSQL in Docker, and database migrations. Pick your platform:

macOS / Linux

curl -fsSL https://raw.githubusercontent.com/Manuekle/senka/main/install.sh | bash

Windows (PowerShell)

irm https://raw.githubusercontent.com/Manuekle/senka/main/install.ps1 | iex

After installation, edit .env with your API keys and start with pnpm dev. When PostgreSQL is available (WORKFLOW_POSTGRES_URL configured), accounts and billing are stored in the database automatically. Without PostgreSQL, the app uses local files.

Local install

Install the dependencies and create your environment file from the example:

corepack enable
pnpm install --frozen-lockfile --strict-peer-dependencies
cp .env.example .env

Open that .env before continuing: replace both example passwords and keep POSTGRES_PASSWORD identical to the password inside WORKFLOW_POSTGRES_URL. No API key goes in the file — the model key and every integration key are set later from Settings and Connections.

Start the database, migrate it, and start the app:

pnpm db:up
pnpm db:migrate
pnpm dev

Open http://localhost:3000 and add your AI provider key and integration keys there. They are stored in ~/.oruka/credentials.json, rotated and cleared from the same screen, and take effect without a restart.

Live verification — Setup (/setup)

This in-app screen checks, live, whether the machine it's running on has everything it needs: Node version, whether Docker is installed and running, the oruka-postgres container, whether WORKFLOW_POSTGRES_URL is reachable, whether the database has its tables, the selected model's key, embeddings, and the .env file. Every failure shows the exact command that fixes it.

The same screen moves configuration between machines: you can import a .env, a .txt, or a JSON file by dropping it or pasting the text, and export everything saved as a grouped .env or as JSON. The export contains secrets in plain text, and the screen says so before you click.

Database

PostgreSQL is the only database the app uses, and there are two ways to give it one. On your own machine, docker-compose.yml starts it on host port 5544 so it doesn't clash with another local Postgres, and POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB and POSTGRES_HOST_PORT configure that container. Pointed at a hosted database — Supabase, Neon, RDS — Docker plays no part: put its connection string in WORKFLOW_POSTGRES_URL and those POSTGRES_* fields stop applying. With Supabase, use the pooler string (Project settings → Database → Connection pooling) and keep the ?sslmode=require. The app works out which case it is from the URL's host, so Setup (/setup) skips the Docker and container checks when the database is remote. All of it is editable from Settings → Database; a real environment variable always beats a value stored there, and changing any of them needs an agent restart.

AI model

By default the app reaches models through the Vercel AI Gateway with AI_GATEWAY_API_KEY. Alternatively OPENAI_API_KEY, ANTHROPIC_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY call the provider directly; AI_PROVIDER picks among the four routes and, left unset, is inferred from whichever key is present. All of them can be added and rotated at any time from Settings → AI model, and are stored in ~/.oruka/credentials.json. Knowledge (RAG) embeddings are the exception: they always run through OpenAI or the Gateway, so an install carrying only an Anthropic or Gemini key needs one of those two as well. Each conversation also has its own model picker in the chat header.

Messaging channels

Web chat works with no setup. WhatsApp and Instagram need a Meta app, created at developers.facebook.com: WhatsApp needs an access token, app secret, phone number ID, and a verify token; Instagram, which doesn't depend on a Facebook page, needs its own token, its own secret — separate from the app's — an account ID, and a verify token, and the account must be a professional one. Each channel's status (connected or missing data) shows live in the dashboard, and credentials are set from Settings.

Knowledge base (RAG)

Knowledge (/knowledge) indexes the business's own documents so the agent answers from them instead of guessing. It accepts PDF, TXT, MD, CSV, TSV, JSON, HTML, XML, YAML, or LOG, up to 20 MB per file — a scanned PDF with no text layer is rejected with a message saying so. Text is split into fixed-size overlapping chunks, turned into embeddings, and stored locally; the agent searches that same index with the search_knowledge tool, so what the page's search box shows is exactly what the agent can find. The same page stores photos, videos, and audio in folders: every file carries a description, and when a customer asks to see something the agent looks it up with find_media and sends it with send_stored_media over WhatsApp or Instagram. WhatsApp sets the limits: 5 MB per photo and 16 MB per video.

Agents and automations

My Agents (/agents) creates your own agents, with their own instructions and tools, with AI help drafting them. Automations (/automations) builds flows triggered by an inbound webhook, also with an AI assistant helping write each step. Both become available to pick from elsewhere in the app once created.

Connections

Connections (/connections) is the inventory of what this install can reach and who said so. The top half is accounts linked by signing in: consent happens on the provider's own domain and no key passes through here. The bottom half is the vendors whose API has no user OAuth, named with the reason on the card rather than hidden behind a Connect button that would only open a form: Stripe and Mercado Pago for charging, Shopify for order lookups, Twilio, SMTP, ElevenLabs, Meta, and the model keys.

Registering a provider's OAuth app (its client ID and secret) is the install's job, not the user's: on Pro and Managed they arrive as environment variables and nobody sees them, and on Enterprise they are entered once under Settings → OAuth apps. Each card opens the form for its own keys without leaving the page. Charging: an automation's "Charge" step uses Stripe or Mercado Pago — Mercado Pago covers ARS, BRL, CLP, COP, MXN, PEN and UYU, Stripe the rest. Shopify connects with a private app's token (read_orders and read_customers) and lets the agent answer "where is my order?" from the store's real orders.

Forms and webhooks

Every form has its own public URL and saves answers step by step, so someone who abandons at question two is already in the inbox with what they did answer. Each form's Webhook card adds an endpoint of yours that every response is POSTed to as JSON, partial ones included. It must be HTTPS on a public host. The response.id field is stable across steps: key on it to upsert rather than collecting one row per step. Connections lists the forms that have one set, but the webhook belongs to the form, not to the account.

Meta Ads

Ads (/ads) brings in the campaigns and leads from your Meta Ads account to view alongside the rest of your conversations, without leaving the app.

Observability

Setting OTEL_EXPORTER_OTLP_ENDPOINT exports Eve and AI SDK trace spans to any OTLP/HTTP-compatible collector. The repository ships Jaeger as a local option:

docker compose --profile observability up -d jaeger
# .env
OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4318"

Full model input and output content is not recorded by default; OTEL_RECORD_INPUTS and OTEL_RECORD_OUTPUTS turn it on, and it's worth reviewing the collector, its access control, and how long it retains data first.

Production deployment

The deploy/ folder provisions a DigitalOcean droplet with Ansible: the two Node services, PostgreSQL, Caddy for TLS, and optionally Beszel for host metrics and Jaeger for traces. In production, the app requires the ROUTE_AUTH_BASIC_USER and ROUTE_AUTH_BASIC_PASSWORD credentials — without both, the routes stay closed. Before each schema migration, Ansible saves a PostgreSQL backup on the server itself.