Skip to main content
OAO’s default development profile runs the console, API, runtime worker, and PostgreSQL on your machine. Development authentication is local, but executing an agent requires an OpenRouter, OpenAI, Anthropic, or xAI connection.
PostgreSQL is the only application database in the local profile. Daytona is needed only for sandbox-enabled agents. WorkOS, S3-compatible storage, and an OTLP collector are optional for a sandbox-disabled first session.

Prerequisites

Install the following before starting OAO:
  • Node.js 22.19.0 or newer
  • pnpm 10.27.0, which is the version pinned by the workspace
  • The Docker CLI and a running Docker-compatible daemon
  • curl for health checks and jq for the API walkthroughs in these docs
Check the installed versions from the repository root:
docker info must reach the Docker daemon. The local scripts use Docker Compose when it is available, fall back to the legacy docker-compose command, and otherwise manage the PostgreSQL container with the Docker CLI directly. A Compose plugin is therefore helpful but not required. On macOS, start Docker Desktop, Colima, or another Docker-compatible daemon before running OAO. For example, if Colima is installed:
If more than one Docker context exists and the active context does not reach Colima, select it and check again:
OAO does not start or stop Docker Desktop or Colima. The current launcher invokes the docker CLI; Apple Container is not implemented as a local runtime for these scripts.

First start

For the recommended interactive path, use the guided quickstart. The steps below describe the manual development workflow. From the repository root, install dependencies:
The checked-in defaults are enough for local development. Copy the environment template only if you want a visible file to edit:
Do not commit .env; it is for local values and may contain provider credentials. Start the complete stack:
On every start, the launcher:
  1. Starts PostgreSQL 17 if it is not already running.
  2. Builds the workspace packages required by the source watchers.
  3. Applies all PostgreSQL migrations.
  4. When AUTH_PROVIDER=development, idempotently seeds the development organization, project, and principal.
  5. Starts the API, runtime worker, and Vite console.
  6. Waits up to 30 seconds for the API, worker, and console to become ready.
Wait for this message before opening the application:
The default services are: Open http://127.0.0.1:8080 in a browser.
Development authentication trusts local requests and seeds a deterministic principal with full local permissions. Use this profile only in a trusted development environment; it is not a production authentication mode.

Disable the development identity

Authentication is selected at process startup. To disable deterministic development login and use WorkOS AuthKit locally, set these values in .env:
Register the exact callback and http://127.0.0.1:8080 sign-out URI in the WorkOS Staging application. Restart pnpm dev:local after changing the provider; the launcher does not switch authentication in a running API process. WorkOS login does not create OAO authorization. Before switching a fresh local database, create the local tenant in development mode, then use the provision:workos command documented in apps/api/README.md to link the WorkOS user and organization to that existing OAO principal and membership.

Normal start and stop

After the first installation, use the same command for normal development:
Use Ctrl+C in that terminal to stop. The launcher terminates the console, API, and runtime worker. Its PostgreSQL behavior depends on who started the database:
  • If this dev:local invocation started PostgreSQL, it stops and removes the PostgreSQL container during shutdown.
  • If PostgreSQL was already running, it leaves PostgreSQL running.
  • In both cases, the named data volume remains intact.
To manage the persistent database separately:
For example, run pnpm db:up before pnpm dev:local when you want PostgreSQL to remain running after you stop the application processes. pnpm db:down stops and removes the OAO PostgreSQL container but does not delete its data volume. Use pnpm dev:local, rather than the generic pnpm dev, when you need the complete local environment. The local launcher owns database startup, migration, seeding, dependency builds, readiness checks, and coordinated shutdown.

Verify the stack

With AUTH_PROVIDER=development, the browser loads the console without a WorkOS sign-in and shows the deterministic development identity. With AUTH_PROVIDER=workos, an unauthenticated browser is redirected to AuthKit and the development identity is unavailable. In both modes, the console uses the HTTP API through the Vite /v1 proxy. The launcher gives the selected provider to the console at startup. Development mode establishes its deterministic local session before loading protected context, while WorkOS mode goes directly to hosted sign-in when no session is present. It does not probe the disabled development-login route, so normal authentication startup is not reported as a missing API route. The console renders in a single light appearance and has no theme toggle. Its neo-brutalist system uses a white ground, ink borders and hard offset shadows, and three flat brand hues with fixed jobs: action red for primary buttons and destructive confirmation, signal yellow for live and completed states, and accent blue for links and focus. Dark surfaces appear compositionally — an ink block for a failed run or a crashed worker — never as a separate theme. Check the services directly:
A healthy default stack returns:
The final console request should return an HTTP 200 response. /healthz reports process liveness. /readyz additionally checks whether the service can use the required PostgreSQL state; the runtime response also identifies the active model profile and sandbox adapter. For repository-wide validation, use:
CI runs these checks with Node.js 22.19.0; use that version when reproducing CI-only failures. The long runtime scenario runs in an isolated process with a three-minute deadline. Its cleanup retains the original error, and the parent terminates the scenario process group so background workers cannot hang the test runner. CI also caps the complete PostgreSQL step at ten minutes. The restart test allows 65 seconds for recovery with pinned Flue 2.0.3: a 30-second ownership lease, up to two 15-second scan intervals (the scan has its own elapsed-time gate), and five seconds for startup/completion. Lease expiry is not an immediate-recovery guarantee. The test still verifies that recovery reuses the original submission and instance, completes on the second attempt, and commits the caller’s result. The database suite also holds a tool-publication replay transaction open while another connection denies or expires its approval. These operations must not block each other; the test checks the resulting tool stage and claim fence, and verifies conflicting publication requests are still rejected. Migration 0041_tool_publication_lock.sql narrows the replay lock without changing the approval policy or claim/result fencing. test:postgres:fresh waits for TCP readiness so the temporary initialization server cannot trigger a premature test start. It creates a PostgreSQL container on a random host port, applies migrations twice, and runs the database and runtime integration suites. Automated tests use explicit provider doubles at adapter boundaries; those doubles are never selectable in the runnable API or console. Both fresh checks remove their temporary container after success, failure, or interruption and do not use the persistent oao-postgres-data volume.

Persistent local data

Both the Compose path and the Docker CLI fallback store PostgreSQL data in the named Docker volume oao-postgres-data. Agents, sessions, transcripts, and debugging history therefore survive dev:local stop/start cycles and pnpm db:down. Inspect the volume without changing it:
To deliberately reset the complete local environment, first stop the stack and use the confirmation-gated CLI command:
Reset permanently deletes oao-postgres-data, .env, and .oao. It refuses to run while OAO services are active. The next pnpm oao setup creates a fresh volume, reapplies migrations, seeds the development tenant, and guides you through provider and starter-agent creation again.
pnpm db:down remains the non-destructive database stop command: it removes the container but preserves the named volume and all local records.

Change local ports

The launcher reads .env when it exists. Keep dependent values aligned when changing ports. This example moves all four services:
Restart pnpm dev:local after editing .env. The console uses Vite’s strict-port mode, so it exits instead of silently choosing a different port when the configured port is occupied.

Configure runtime providers

Runtime execution requires an organization-shared model provider and a organization-shared Daytona provider. There is no credential-free fake execution profile.

OpenRouter, OpenAI, Anthropic, and xAI models

Generate one platform encryption key and set it for both the API and runtime:
Restart the API and runtime, then add an OpenRouter, OpenAI, Anthropic, or xAI provider on the console Models page. Enter provider API keys there; OAO encrypts them before storing them in PostgreSQL for the current organization and project. Add a model preset linked to that connection. The model selector loads the live catalog available to the selected provider key; for direct providers it offers the returned models supported by OAO’s runtimes. xAI uses its live language-model catalog so newly listed text-output Grok IDs can be selected without an OAO catalog update. OpenAI also discovers new supported GPT and o-series text/reasoning models from its live account list, including GPT-6 Astra, without a library update. See model discovery limitations for models with unknown metadata. No process restart is needed for later provider or preset additions. Direct OpenAI presets also pin plain-text output, reasoning mode and effort, verbosity, and reasoning-summary settings. Direct Anthropic presets pin adaptive or disabled thinking, a total output-token ceiling, and a supported effort level. Supported direct xAI reasoning presets pin text output and a documented Grok reasoning effort. The runtime applies these settings to every session using that preset.
Do not set OPENROUTER_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, or XAI_API_KEY in .env. Provider keys are write-only console/API inputs and are never returned after encryption.

Daytona sandboxes

Use the same platform encryption key configured for hosted model providers, restart the API and runtime, then open Sandbox providers in the console. Select Add sandbox provider, choose Daytona, and add the API key, an optional target preference, and the domain/CIDR allowlist agents may use with network: "restricted". The key is a write-only input encrypted in PostgreSQL for the current project. Choose the connection, one of the active snapshots returned by Daytona, the network policy, and exact sandbox capabilities while creating an agent or before publishing a later version. No process restart is needed for later connection, credential, target, or allowlist changes. The connection’s optional target is only a provider placement preference. OAO does not verify residency or treat the target as a residency guarantee. A restricted egress policy must contain at least one allowed domain or CIDR; network: "none" blocks sandbox network access. See Daytona sandboxes for the API and tool-capability contracts.

S3-compatible attachments and workspace backups

To submit run attachments or preserve Daytona files after a sandbox is deleted, open Storage providers and add an organization-shared S3-compatible connection. The API and runtime use the same OAO_CREDENTIAL_ENCRYPTION_KEY to protect its write-only credentials. The first connection becomes the organization default automatically; you can explicitly select another default later. Attachment bytes go directly to this object store and are never persisted in PostgreSQL. OAO asks Daytona for the thread sandbox’s active workdir, archives that directory after each completed agent run, and restores the latest verified archive into a new replacement sandbox’s workdir. No restart is required after adding or rotating a storage connection. See Workspace backups for retention, encryption, size, and failure behavior.

Event webhooks to a local receiver

Event webhooks require public HTTPS endpoints. To deliver to a receiver on your machine, such as a local Convex backend, add this to .env and restart pnpm dev:local:
It allows http:// and private-network endpoints in both the API and the runtime worker, and both refuse to start with it unless NODE_ENV=development. Event commits wake the worker immediately. It also polls for undelivered events every second as a fallback; set OAO_EVENT_WEBHOOK_POLL_MS to change that interval.

OTLP telemetry

To export vendor-neutral telemetry, set the collector endpoint:
Leave it empty when no collector is running.
pnpm dev:local reads AUTH_PROVIDER and NODE_ENV from .env. Development remains the credential-free default. Local WorkOS testing requires NODE_ENV=development; hosted WorkOS deployments should use production mode with HTTPS origins, callbacks, and cookies.

Troubleshooting

Cannot connect to the Docker daemon

Symptoms include Cannot connect to the Docker daemon, a missing socket, a Docker context error, or pnpm db:up failing before PostgreSQL starts.
  1. Check the selected context and daemon:
  2. Start Docker Desktop or, when using Colima, run:
  3. Run pnpm dev:local again only after docker info succeeds.
Starting OAO does not start the daemon for you. If a colima context is missing, starting Colima normally creates or restores it; then recheck the active Docker context.

pnpm install reports ignored build scripts

pnpm 10 may finish successfully while warning that dependency lifecycle scripts were ignored. In this checkout, the warning can name @google/genai, esbuild, and protobufjs. The warning by itself is not a failed installation; pnpm dev:local immediately runs the workspace build, which is the relevant functional check. Do not approve an unfamiliar dependency automatically. If startup later fails specifically because a reviewed dependency’s build script did not run, use pnpm’s interactive approval flow and reinstall:
Select only dependencies whose install scripts you have reviewed and intend to trust. The approval can update tracked pnpm policy files, so inspect git diff before committing any result.

A port is already in use

The default ports are 5432, 3000, 8788, and 8080. Find the process holding a port:
Stop the conflicting process or change the corresponding values in .env. For PostgreSQL, change both OAO_POSTGRES_PORT and DATABASE_URL. For the API, keep OAO_API_PORT, API_ORIGIN, and VITE_OAO_API_PROXY_TARGET aligned. For the console, keep OAO_CONSOLE_PORT and APP_ORIGIN aligned.

PostgreSQL does not become healthy

Inspect the container and its health output:
When Compose is active, its generated container name may differ from oao-postgres-local; use the PostgreSQL container name shown by docker ps -a with docker logs. Check that the host port in DATABASE_URL matches OAO_POSTGRES_PORT. Do not delete the volume as a first troubleshooting step, because it contains all persistent local OAO data.

A service never becomes ready

The launcher stops the stack if the API, runtime worker, or console does not become ready within 30 seconds. Read the first service error printed above the timeout, then check:
  • PostgreSQL is healthy and reachable through DATABASE_URL.
  • No other process is using an application port.
  • API and runtime use the same canonical 32-byte OAO_CREDENTIAL_ENCRYPTION_KEY.
  • The project has a configured model provider, an approved model preset, and a Daytona connection.
  • The selected immutable agent version references those project provider keys.
Correct the provider configuration, restart pnpm dev:local if the platform encryption key changed, and rerun the health checks.

Codex worktrees

The repository’s .codex/environments/environment.toml defines the OAO local Codex environment. Its setup script copies .env from the checkout on main when a new worktree has no .env, chooses available Console, API, and runtime ports, updates their local URLs, and installs dependencies with the lockfile. Existing worktree credentials are preserved on reruns. The Run action starts pnpm dev:local. Setup requires Python 3.9+, pnpm, and a checkout of main with an existing .env. This changes application ports, not database isolation: worktrees retain the source database URL and workers consume the same run queue. Port availability is checked during setup; if another process takes a chosen port before startup, rerun setup or adjust the worktree’s .env. Environment files remain untracked and are written with permissions restricted to the current user.