> ## Documentation Index
> Fetch the complete documentation index at: https://haico.gr/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Local installation

> Run the full HAI-Co² stack on your machine with Docker Compose: prerequisites, a one-command bring-up, configuration, and first login.

HAI-Co² runs locally as a **Docker Compose** stack, so you do not install Python or Node on the
host. Everything lives under [`deployment/local/`](https://github.com/petrosrapto/HAICO/blob/main/deployment/local), and a single script brings the whole
system up, applies database migrations, and health-checks every service.

## Prerequisites

The Dockerized flow needs only one thing:

* **Docker Engine 24+** with the **Compose v2** plugin. Verify with `docker --version` and `docker compose version`.

The images carry their own runtimes (backend on Python 3.11, frontend on Node 20), so no host
Python or Node is required.

<Note>
  The local frontend image is a **production build** (`node server.js`), not `next dev`, so there is
  **no hot reload**: after editing backend or frontend source you must rebuild with
  `docker compose up -d --build` (or re-run the deploy script).
</Note>

## Quickstart

<Steps>
  <Step title="Get the code and enter the local deployment">
    ```bash theme={null}
    git clone https://github.com/petrosrapto/HAICO.git
    cd HAICO/deployment/local
    chmod +x scripts/*.sh
    ```
  </Step>

  <Step title="Create the env files">
    `deploy.sh` copies them from the examples on first run, or do it yourself:

    ```bash theme={null}
    cp backend/.env.example backend/.env
    cp frontend/.env.example frontend/.env
    ```
  </Step>

  <Step title="Set the essentials in backend/.env">
    Set a long random `JWT_SECRET`, an `ADMIN_DEFAULT_PASSWORD` (this seeds the `admin` login on the
    first migration), and at least one LLM key (e.g. `OPENAI_API_KEY`). Leave `RECAPTCHA_SECRET_KEY`
    and `SMTP_HOST` empty so local dev skips captcha and email verification.
  </Step>

  <Step title="Bring the stack up">
    ```bash theme={null}
    ./scripts/deploy.sh
    ```

    This sources both `.env` files, runs `docker compose up -d --build`, then health-checks Postgres,
    the backend, and the frontend. The equivalent manual command is `docker compose up -d --build`.
  </Step>

  <Step title="Log in">
    Open [http://localhost:3000](http://localhost:3000), sign in at `/login` with username **`admin`**
    and the `ADMIN_DEFAULT_PASSWORD` you set (this account is pre-verified), then open the Studio at
    `/app`.
  </Step>
</Steps>

## What comes up

`deploy.sh` runs the Compose file at [`deployment/local/docker-compose.yml`](https://github.com/petrosrapto/HAICO/blob/main/deployment/local/docker-compose.yml), which starts four services:

| Service    | Image / build                                                                                      | URL                                            | Notes                                                                                           |
| ---------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `postgres` | `postgres:15-alpine`                                                                               | `localhost:5432`                               | Workspace + agent memory; volume `haico_pg_data`; backend waits on its `pg_isready` healthcheck |
| `backend`  | builds [`backend/Dockerfile`](https://github.com/petrosrapto/HAICO/blob/main/backend/Dockerfile)   | [http://localhost:8000](http://localhost:8000) | FastAPI; Swagger at `/docs`, health at `/health`                                                |
| `frontend` | builds [`frontend/Dockerfile`](https://github.com/petrosrapto/HAICO/blob/main/frontend/Dockerfile) | [http://localhost:3000](http://localhost:3000) | Next.js production build; proxies `/api` to the backend                                         |
| `phoenix`  | `arizephoenix/phoenix`                                                                             | [http://localhost:6006](http://localhost:6006) | Agent trace UI (login `admin@localhost` / `haicoadmin`); optional                               |

On first start the backend's [`entrypoint.sh`](https://github.com/petrosrapto/HAICO/blob/main/backend/entrypoint.sh) waits for Postgres, runs `alembic upgrade head` (creating the
schema and seeding the admin user), runs `python -m app.db.init_checkpointer` (creating the
LangGraph checkpointer tables), and then starts uvicorn.

## Configuration

Settings resolve in the order **env var → `.env` → YAML → defaults** (see [`backend/app/core/config.py`](https://github.com/petrosrapto/HAICO/blob/main/backend/app/core/config.py)):

* **`backend/.env`** (secrets): `JWT_SECRET`, `ADMIN_DEFAULT_PASSWORD`, LLM API keys, optional `SMTP_*`, `RECAPTCHA_SECRET_KEY`, `GOOGLE_OAUTH_CLIENT_ID`. Compose assembles `DATABASE_URL` from the `POSTGRES_*` values, so you do not set it directly.
* **`frontend/.env`**: `API_PROXY_TARGET` (default `http://backend:8000`) and the `NEXT_PUBLIC_*` keys, baked into the image **at build time** (so changing them needs a rebuild).
* **`deployment/local/backend/config.local.yaml`** (non-secret): the default LLM `provider`/`model`, CORS origins, and `observability.provider` (`none` to disable Phoenix/LangSmith tracing). Secrets never come from YAML.

<Warning>
  **`CONFIG_PATH` gotcha.** The YAML is found by scanning a hardcoded `CONFIG_PATHS` list; an explicit
  `CONFIG_PATH` env var is prepended and wins. The Compose file sets `CONFIG_PATH=/app/config.local.yaml`
  and mounts the file there, so it resolves. If you craft a custom container where `CONFIG_PATH` points
  at a path that does not exist, none of the relative fallbacks match the container filesystem and the
  YAML is **silently ignored**, falling back to `Settings` defaults.
</Warning>

## First login and accounts

* **Seeded admin** is the practical way in: the first migration creates user `admin` (role admin, `email_verified=true`) using your `ADMIN_DEFAULT_PASSWORD`.
* **Self-registration** (`/login` → register) enforces email verification before login, so without `SMTP_*` configured a self-registered local user cannot log in. Use the admin account, or configure SMTP.
* **Captcha** is skipped automatically when `RECAPTCHA_SECRET_KEY` is empty.

<Warning>
  `ADMIN_DEFAULT_PASSWORD` seeds the admin **only on the first migration** (empty database). Changing
  it later has no effect until you drop the Postgres volume (`./scripts/cleanup.sh --volumes`) and let
  migrations re-run.
</Warning>

## Day-to-day

```bash theme={null}
./scripts/deploy.sh --status      # health + container status
./scripts/deploy.sh --logs        # follow logs
./scripts/deploy.sh --restart     # restart the stack
docker compose logs -f backend    # one service (run from deployment/local)
docker compose exec backend bash  # shell into a container
docker compose up -d --build      # rebuild after a source change (no hot reload)
```

## Resetting

```bash theme={null}
./scripts/cleanup.sh              # stop, keep data + images (= --down)
./scripts/cleanup.sh --volumes   # also drop haico_pg_data (re-seeds admin on next up)
./scripts/cleanup.sh --images    # also remove the built images
./scripts/cleanup.sh --all       # everything
```

For how this same stack is built and shipped to the dev and production servers, see
[Deployment](/docs/deployment).
