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:
# Backend: uv sync + build Postgres image + up + migrate + bootstrap
make setup
# Frontend: pnpm install for packages/web
make web-installmake 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:
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:
ACASO_CONSOLE_ORIGINS=http://localhost:3000,http://localhost:3001Restart any running AcasoOS server so the change takes effect.
Run the stack
Three terminals:
# Terminal 1: DataHub on :8000
make run
# Terminal 2: AgentHub on :8001 (chat + admin)
make run-agenthub
# Terminal 3: Console on :3000
make web-devTip: AgentHub is the unified gateway in dev — it mounts every DataHub router too. A single connection to
http://localhost:8001serves the whole console surface.
Connect the console
Open http://localhost:3000. You will be redirected to /connect. Submit:
| Field | Value |
|---|---|
| Name | "Localhost" |
| URL | http://localhost:8001 |
| Long-lived API key | The 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:
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
# Backend unit + integration tests
make test
# Frontend unit tests (vitest)
make web-test
# Frontend e2e (playwright)
pnpm --filter @acaso/web exec playwright testThe 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
make web-build
ls packages/web/out/Serve it with any static server to mimic the production deploy:
npx serve packages/web/out -p 4000This is exactly what AWS Amplify hosts at os.aca.so in production. See Deployment.