The handbook
TroubleshootingLocal developmentDeploymentProvision a new clientStandalone deploymentUpgradesArchitecture
Operate

Local development

Bring up the full stack on your laptop: AcasoOS backend plus the console pointed at it.

Prereqs

  • macOS or Linux with Docker.
  • Node ≥ 20 and pnpm ≥ 9: brew install node && corepack enable && corepack prepare pnpm@latest --activate.
  • uv ≥ 0.11 and Python 3.14: brew install uv && uv python install 3.14.

First-time setup

From the repo root:

bash
# Backend: uv sync + build Postgres image + up + migrate + bootstrap
make setup

# Frontend: pnpm install for packages/web
make web-install

make setup prints the bootstrap admin API key. Save it; it works for your first connection.

Mint a personal key

For day-to-day work you want your own per-human key, not the bootstrap:

bash
make mint-key USER_EMAIL=you@acaso.com USER_NAME="You"

Store the printed key in your password manager.

Configure CORS

Add the local console origin to your AcasoOS .env:

bash
ACASO_CONSOLE_ORIGINS=http://localhost:3000,http://localhost:3001

Restart any running AcasoOS server so the change takes effect.

Run the stack

Three terminals:

bash
# Terminal 1: DataHub on :8000
make run

# Terminal 2: AgentHub on :8001 (chat + admin)
make run-agenthub

# Terminal 3: Console on :3000
make web-dev

Tip: AgentHub is the unified gateway in dev — it mounts every DataHub router too. A single connection to http://localhost:8001 serves the whole console surface.

Connect the console

Open http://localhost:3000. You will be redirected to /connect. Submit:

FieldValue
Name"Localhost"
URLhttp://localhost:8001
Long-lived API keyThe key from make mint-key

Click Add connection. You should land on /agents with the chrome showing acaso · You · localhost / default.

Verify in DevTools → Application → Storage that the long-lived key is NOT in any storage tier. Only the session token is, under sessionStorage.

Verify session expiry handling

To trigger the session-expired flow without waiting 8 hours, drop the session row's expiry from a SQL shell:

sql
UPDATE dh_api_keys
   SET expires_at = NOW() - INTERVAL '1 minute'
 WHERE kind = 'session'
   AND parent_key_id = '<your-long-lived-key-id>';

Your next click in the console returns 401 and the console redirects to /connect with the URL pre-filled and the "Session expired" banner.

Run the test suites

bash
# Backend unit + integration tests
make test

# Frontend unit tests (vitest)
make web-test

# Frontend e2e (playwright)
pnpm --filter @acaso/web exec playwright test

The Playwright e2e spec asserts the property that after Connect submits, the long-lived key is not present in any browser storage tier.

Build a production bundle locally

bash
make web-build
ls packages/web/out/

Serve it with any static server to mimic the production deploy:

bash
npx serve packages/web/out -p 4000

This is exactly what AWS Amplify hosts at os.aca.so in production. See Deployment.

PreviousOperateTroubleshootingNext OperateDeployment