
# Install

On a Linux host, as a sudoer:

```bash
curl -fsSL https://acahti.benchyard.com/install.sh | bash -s -- --domain acahti.example.com
```

The script installs Docker if it is missing, pulls the release image from `ghcr.io/acahti-ai/acahti`, copies its compose file and host scripts into `~/acahti`, generates secrets into `~/acahti/.env`, and starts the instance. Nothing is built on the host.

Or let your coding agent do it:

```text
Install https://acahti.benchyard.com/install.md
Self-host Acahti on my infrastructure. Ask me for the domain, SSH access, and Runner host.
```

## Have ready

- A Linux host with sudo and at least 2 GiB RAM.
- A public HTTPS hostname. You terminate TLS at your own reverse proxy or tunnel and forward it to the gateway.
- Optionally, a second host for the **Runner**, reachable over SSH from the first. CI never runs on the control plane.

## Options

| Option | `.env` | Meaning |
|---|---|---|
| `--domain HOST` | `DOMAIN`, `ROOT_URL` | Public hostname; required on first install |
| `--org NAME` | `ACAHTI_ORG` | Organization repositories live under (default `acme`) |
| `--runner SSH` | `ACAHTI_BUILD` | Runner host, set up over SSH, such as `ci@10.0.0.5` |
| `--bind ADDR` | `GATEWAY_BIND` | Gateway listen address (default `127.0.0.1:8080`; `0.0.0.0:8080` when a Runner is set) |
| `--version X.Y.Z` | `ACAHTI_VERSION` | Release to install (default: the latest) |
| `--dir PATH` | | Install directory (default `~/acahti`) |

Options are written to `.env` and kept for later runs. Data lives under `ACAHTI_DATA` (default `/var/lib/acahti`).

## After install

1. Point your proxy or tunnel at the gateway and open the instance URL.
2. Invite members from the web app. Each person joins at `<instance>/join`.
3. Paste the agent prompt from the instance home page into a coding agent, open the Board, and watch it run the [demo loop](/demo.md).

## Upgrade

Back up PostgreSQL and `ACAHTI_DATA`, then run the installer again:

```bash
curl -fsSL https://acahti.benchyard.com/install.sh | bash
```

It installs the latest release, keeps `.env` and data, and resyncs the Runner. `--version` pins a release. An instance older than 0.15.0 upgrades to 0.15.2 first, then to the latest release. Never `docker compose down -v` on a live instance.

## Release delivery

The gateway, MinIO, Caddy edge, Gotenberg, Tika and ONLYOFFICE Docs are separate
images. MinIO uses `ghcr.io/acahti-ai/acahti:minio-<MINIO_IMAGE_VERSION>`, edge
uses `caddy:<CADDY_VERSION>-alpine`, Gotenberg uses
`gotenberg/gotenberg:<GOTENBERG_VERSION>` for Office previews, Tika uses
`apache/tika:<TIKA_VERSION>` to extract searchable text from PDF and Office
files, and `onlyoffice/documentserver:<ONLYOFFICE_VERSION>` edits knowledge
files in the browser. Those versions are pinned in `versions.env`; application
releases reuse them. For an offline install, load all six image archives before
running the installer. ONLYOFFICE needs up to 3 GiB of memory and a minute or
two after start before documents open in the editor; until then they show their
PDF preview. A proxy in front of the edge must accept request bodies of at least
200 MiB for uploads. The
installer reuses cached images and pulls only on a cache miss.

Upgrades reconcile changed services and wait for health. Runtime configuration and remote Runner bundles are synchronized only when their inputs change; a gateway release alone does not reinitialize them.

## Private registry access

For a private mirror, authenticate Docker with an account that can read the
registry before running the installer (`docker login <registry>`). Use a read-only package
credential or a registry credential helper; do not place credentials in the
installation's `.env`, Git repository, command examples or shared archives.

### Gateway updates

The stable `edge` service owns the published HTTP port. During an update the
single gateway first returns 503 from its readiness probes, while continuing to
serve requests already dispatched by edge. After two seconds it closes event
streams and drains ordinary requests for up to 25 seconds. Compose allows 30
seconds before killing the container. The replacement starts only after the old
process exits, so file-backed credentials and background jobs never have two
writers.

Caddy checks readiness every 250 milliseconds. Requests arriving during the
replacement wait for an upstream for up to 120 seconds. Only GET and HEAD may be
retried after a failed upstream round trip. A write can wait after a dial failure
without sending its body; writes already sent upstream are never replayed. SSE
clients reconnect after the old stream closes. This does not provide durable
replay of events emitted while a client is disconnected.

This is a bounded waiting strategy: normal updates avoid gateway errors but add
latency. Clients must allow enough time for the handoff. Requests exceeding the
drain deadline, clients with shorter timeouts, failed startup, and outages beyond
the edge retry budget can still fail. It is not multi-replica high availability.
Changes to edge or to stateful dependencies
also need a maintenance window; the request-waiting guarantee covers gateway-only updates.

The installer pins the gateway to its downloaded digest (or local image ID for
an offline archive), preserves a protected `.previous-release.*` host bundle,
and restores that bundle on failure. `scripts/upgrade.sh` downloads images before
replacement and restores the previous gateway image and environment if readiness
fails. A failed upgrade still exits unsuccessfully, including after a successful
rollback. Database migrations must be reviewed separately: image rollback does
not undo database changes. Back up the database and persistent data before an
upgrade that changes them.

`CADDY_VERSION` pins the official public `caddy:<version>-alpine` edge image.
A normal installation downloads it automatically. Operators using an internal
registry can mirror that version and set `CADDY_IMAGE` to its repository name;
this is an installation setting, not a product fork. Offline installs must load
all six image archives. Leave the edge image and configuration unchanged
during gateway-only updates.
