# Installation

> Install the tapes server and tapesctl client, bootstrap local PostgreSQL and Ollama, and verify with tapes version and tapes local status.

- Repository: papercomputeco/tapes
- GitHub: https://github.com/papercomputeco/tapes
- Human docs: https://grok-wiki.com/public/docs/papercomputeco-tapes-020596f21750
- Complete Markdown: https://grok-wiki.com/public/docs/papercomputeco-tapes-020596f21750/llms-full.txt

## Source Files

- `install.sh`
- `docs/installation.md`
- `cmd/tapes/local/local.go`
- `cmd/tapes/init/init.go`
- `cmd/tapes/version/version.go`
- `makefile`

---

---
title: "Installation"
description: "Install the tapes server and tapesctl client, bootstrap local PostgreSQL and Ollama, and verify with tapes version and tapes local status."
---

Tapes ships as two binaries. `tapes` is the server: it owns PostgreSQL, runs proxy / read API / ingest / workers, and writes `.tapes/` configuration. `tapesctl` is a separate client (repository `papercomputeco/tapesctl`) that captures sessions and reads them back. Release installs come from `https://download.tapes.dev`; local dependencies are started with `tapes local up` (Docker required).

## Prerequisites

| Need | Why |
| --- | --- |
| `curl`, `uname`, writable `/tmp` | `install.sh` download path |
| Linux or macOS (`darwin`) | Script maps `uname -s`; other OS values exit `Unsupported OS` |
| `amd64` or `arm64` | `x86_64` → `amd64`; `aarch64`/`arm64` → `arm64`; other arches exit |
| `sudo` (default install) | Moves the binary to `/usr/local/bin` |
| Docker | Required for `tapes local`; missing `docker` on `PATH` fails with `docker is required for 'tapes local'` |

Ollama is optional on the host. Native Ollama is preferred when it is already serving; otherwise the bootstrap starts `tapes-local-ollama`. PostgreSQL is not optional for a working server.

## Install the binaries

<Steps>
<Step title="Install the server">

```bash
curl -fsSL https://download.tapes.dev/install | bash
tapes version
```

The script downloads `https://download.tapes.dev/$TAPES_VERSION/$OS/$ARCH/tapes` (`TAPES_VERSION` defaults to `latest`), `chmod +x`, and `sudo mv` into `TAPES_INSTALL_DIR` (default `/usr/local/bin`). It then runs `tapes version` as the smoke test.

</Step>
<Step title="Install the client">

```bash
curl -sSfL https://download.tapes.dev/tapesctl/install | bash
tapesctl version
```

`tapesctl` is not built from this repository. Do not re-add client commands (`start`, `export`, `seed`, `sessions`, `search`, `skill`) to `tapes`.

</Step>
<Step title="Confirm PATH">

Each `version` call must print and exit 0. `tapes version` writes:

```text
Version: <tag or git describe>
Sha: <commit>
Built at: <UTC timestamp>
```

Those fields are linker-stamped (`pkg/utils.Version`, `Sha`, `Buildtime`). A missing command means the install directory is not on `PATH`.

</Step>
</Steps>

<ParamField body="TAPES_VERSION" type="string">
Release selector used by `install.sh`. Default `latest`. Example: `TAPES_VERSION=v0.17.0`.
</ParamField>

<ParamField body="TAPES_INSTALL_DIR" type="string">
Destination directory for the `tapes` binary. Default `/usr/local/bin`. Must be writable via `sudo` (or already owned if you change it).
</ParamField>

<Tip>
Soft update checks (not `tapes version`) GET `https://download.tapes.dev/latest/version`. Disable with `--disable-update-check` or `update.disabled`. Dev / nightly / `git describe` infix builds skip the prompt.
</Tip>

## Bootstrap PostgreSQL and Ollama

```bash
tapes local up
tapes local status
```

Bare `tapes local` is the same as `tapes local up`. Docker must be available.

### What starts

| Resource | Default |
| --- | --- |
| Docker network | `tapes-local` |
| Postgres container | `tapes-local-postgres` |
| Postgres image | `public.ecr.aws/g4e5l3z3/papercomputeco/postgres:17.7-pgduckdb-1.1.1` |
| Host port | `5432` → container `5432` |
| Role / password / database | `tapes` / `tapes` / `tapes` |
| DSN written to config | `postgres://tapes:tapes@localhost:5432/tapes?sslmode=disable` |
| Data directory | `<active .tapes>/postgres` mounted at `/tapes-postgres`, `PGDATA=/tapes-postgres/data` |
| Ollama container (when used) | `tapes-local-ollama` from `ollama/ollama:latest` |
| Ollama host port | `11434` → container `11434` |
| Ollama model volume | `~/.ollama` → `/root/.ollama` |
| Embedding model pulled | `embeddinggemma:latest` |
| Model name stored in config | `embeddinggemma` |

Postgres readiness is `pg_isready` for up to 30s. Containerized Ollama is polled with `ollama list` for up to 60s, then `ollama pull embeddinggemma:latest`.

### Ollama placement

`tapes local up` does not always start a container. The plan is:

| Condition | Result |
| --- | --- |
| `--docker-ollama` or `tapes-local-ollama` already exists | Use the Docker container |
| Native server answers `GET http://127.0.0.1:<port>/api/version` with 200 | Reuse native Ollama; pull the model with host `ollama pull` |
| `ollama` is on `PATH` but the port is silent | Print a warning: start Ollama yourself (`ollama serve` or the app) and `ollama pull embeddinggemma:latest`. No container is started |
| No native install | Pull and start `tapes-local-ollama` |

Native is preferred because containerized Ollama does not get GPU access on macOS, and a published container on the same port can shadow a loopback-only native server.

```bash
tapes local up --docker-ollama
```

### Config written on success

On success the command persists settings into the active `.tapes/config.toml` so later `tapes serve` needs no DSN or embedding flags:

| Key | Value |
| --- | --- |
| `storage.postgres_dsn` | Local DSN above |
| `vector_store.target` | Same DSN |
| `proxy.upstream` | `http://127.0.0.1:11434` or `http://localhost:11434` |
| `embedding.provider` | `ollama` |
| `embedding.target` | Same Ollama URL |
| `embedding.model` | `embeddinggemma` |

If the write fails, the command prints `tapes config set embedding.target …` and `tapes serve --postgres "<dsn>"` instead of `tapes serve`.

<ParamField body="--postgres-port" type="int">
Host port for Postgres. Default `5432`. Persistent on `tapes local` and subcommands.
</ParamField>

<ParamField body="--ollama-port" type="int">
Host port for Ollama. Default `11434`.
</ParamField>

<ParamField body="--postgres-image" type="string">
Override the pinned Postgres image (must still provide pgvector + pg_duckdb for the product path).
</ParamField>

<ParamField body="--ollama-image" type="string">
Override the Ollama image. Default `ollama/ollama:latest`.
</ParamField>

<ParamField body="--docker-ollama" type="boolean">
Force the Ollama container even when a native install is serving.
</ParamField>

<ParamField body="--config-dir" type="string">
Root command flag. Overrides `.tapes/` resolution for data + config (`--config-dir` → `./.tapes` → `~/.tapes`).
</ParamField>

### Status, stop, wipe

```bash
tapes local status
tapes local down
tapes local down --wipe
```

`tapes local status` inspects `tapes-local-postgres` and `tapes-local-ollama` via `docker ps -a`. If the Ollama container is missing but a native server answers `/api/version`, it prints `ollama (native)` and the loopback URL. Otherwise a missing name is `not created`.

`tapes local down` `docker rm -f` both containers. Data under `<.tapes>/postgres` stays. `--wipe` then `RemoveAll`s that directory and permanently deletes locally captured sessions.

<Warning>
`--wipe` is irreversible for the local Postgres volume. Ollama models in `~/.ollama` are not deleted.
</Warning>

## Optional project-local config

```bash
tapes init
tapes init --preset ollama
tapes init --preset openai
tapes init --preset anthropic
tapes init --preset https://example.com/config.toml
```

`tapes init` creates `./.tapes/config.toml` (mode `0600`) in the current working directory. That directory then wins over `~/.tapes/` for server commands. Presets: `openai` (upstream `https://api.openai.com`), `anthropic` (`https://api.anthropic.com`), `ollama` (`http://localhost:11434`). An `http://` or `https://` preset is fetched and parsed as TOML.

`tapes local up` without a pre-existing directory creates `~/.tapes` and stores Postgres data there. A cwd `.tapes/` created by `init` is used instead.

`tapesctl` does **not** read project-local `.tapes/`. It uses `~/.tapes/config.toml` and the single key `tapes-url`.

## Verify

<RequestExample>
```bash
tapes version
tapes local status
```
</RequestExample>

Expected after a successful Docker bootstrap:

- `tapes version` prints `Version`, `Sha`, `Built at`
- `tapes local status` shows `tapes-local-postgres` as Up
- Ollama is either `tapes-local-ollama` Up or `ollama (native) http://127.0.0.1:11434`

`tapes status` is a different command: it reports config dir, provider → upstream, whether `storage.postgres_dsn` is set, `client.api_target` (default `http://localhost:8081`), and probes the read API (`/v1/stats`, 3s timeout). Before `tapes serve`, API is expected to be unreachable.

After you start the stack (`tapes serve`), defaults are proxy `:8080`, read API `:8081`, private ingest `:8082`, embedding worker on. `GET http://localhost:8081/ping` returns JSON `"pong"`. Point the client at the **read** API once:

```bash
tapesctl config set tapes-url http://localhost:8081
```

Capture commands must target ingest `:8082` explicitly. Continue on [Quickstart](/quickstart).

## Source builds

Use this only when you are developing this repository. Go **1.26+** and `GOEXPERIMENT=jsonv2` are required (`make build-local` sets both; Nix `flake.nix` exports `GOEXPERIMENT=jsonv2`).

<CodeGroup>
```bash title="Make"
make build-local
./build/tapes version
./build/tapes local up
make install   # install -m 0755 ./build/tapes $(go env GOBIN)/tapes
```

```bash title="Nix flake"
nix develop
make help
make build-local
```
</CodeGroup>

`make install` copies via `install(1)` (new inode) so an in-place overwrite does not invalidate a running macOS Mach-O signature. Contributor tests go through `make test` (Dagger + pinned Postgres). See [Contribute](/contribute).

## Compose and container image

`docker-compose.yaml` starts the same Postgres image, `ollama/ollama`, and a `Dockerfile`-built `tapes` that runs `serve` against in-network `ollama:11434`. Published host ports are **8080 and 8081 only**; ingest `:8082` is not mapped in that file.

The image is a static `CGO_ENABLED=0` `./cli/tapes` binary (`EXPOSE 8080`, entrypoint `/app/tapes`). `tapes-extproc` is a separate image and is not part of local install.

## Embeddings without Ollama

PostgreSQL is still required. To use OpenAI embeddings instead of the local Ollama model:

```bash
tapes auth openai
tapes config set embedding.provider openai
```

`OPENAI_API_KEY` is accepted instead of `tapes auth openai`. Model and `embedding.dimensions` (default `768` for `embeddinggemma`) must match the provider. Store keys in `.tapes/credentials.toml`, not `config.toml`. Details: [Configure embeddings](/configure-embeddings).

## Common failures

| Symptom | Cause / fix |
| --- | --- |
| `Unsupported OS` / `Unsupported architecture` | `install.sh` only maps linux/darwin and amd64/arm64 |
| `tapes: command not found` after install | `TAPES_INSTALL_DIR` not on `PATH`, or `sudo mv` failed |
| `docker is required for 'tapes local'` | Install Docker and ensure `docker` is on `PATH` |
| Native Ollama warning, no container | Binary is installed but `/api/version` is down — start it and pull `embeddinggemma:latest`, or pass `--docker-ollama` |
| Postgres not ready within 30s | Port `5432` already bound; try `--postgres-port` |
| Ollama container not ready within 60s | Image pull / daemon issue; `tapes local status` and Docker logs |
| `tapes status` API unreachable | Expected until `tapes serve`; not a substitute for `tapes local status` |
| Source build / merkle JSON errors | Build without `GOEXPERIMENT=jsonv2` — use `make build-local` |
| `--wipe` emptied sessions | Intended: the `<.tapes>/postgres` directory was deleted |

More operational failures (wrong capture port, ingest 413, missing OpenAI keys): [Troubleshooting](/troubleshooting).

## Next

<CardGroup>
<Card title="Quickstart" href="/quickstart">
Start `tapes serve`, point `tapesctl` at `:8081`, seed demo data, and capture against `:8082`.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
Dotdir resolution, flag / `TAPES_` / `config.toml` precedence, and defaults.
</Card>
<Card title="CLI reference" href="/cli-reference">
`tapes` commands: `init`, `local`, `serve`, `status`, `auth`, `config`, `version`.
</Card>
<Card title="Configure embeddings" href="/configure-embeddings">
Switch `embedding.provider` between `ollama` and `openai`, and run or disable the embed worker.
</Card>
<Card title="Contribute" href="/contribute">
Nix or Make source builds, `GOEXPERIMENT=jsonv2`, and `make test` through Dagger.
</Card>
</CardGroup>
