# Conversation history

Agent chat history is stored in **browser localStorage** by default. No server setup is needed. Enable server-side persistence when you want history shared across devices, preserved after a browser clear, or visible to multiple users.

## How persistence works

```mermaid
flowchart TD
    A[User sends message] --> B{VITE_FEATURE_CONVERSATION_DB=true?}
    B -- No --> C[Browser localStorage]
    B -- Yes --> D{Authenticated?}
    D -- No --> C
    D -- Yes --> E{Store configured?}
    E -- auto --> F[Auto-select backend]
    E -- explicit --> G[Use named backend]
    F --> H[(Server store)]
    G --> H
```

- **Persistence off** — every conversation is local to the browser. Clearing browser data loses history.
- **Persistence on, unauthenticated user** — falls back to localStorage. Server stores require a user identity to scope history correctly.
- **Persistence on, authenticated user** — history is read and written to the configured server store.

## Enable server persistence

Server-side persistence requires two things:

1. `VITE_FEATURE_CONVERSATION_DB=true` must be set **at build time** (before `bun run build`). This is a `VITE_*` variable — it is baked into the bundle and cannot be changed at runtime. It also requires Clerk auth to be active (`VITE_AUTH_PROVIDER=clerk`).
2. A backend must be reachable at runtime.

```bash
## In CI / build environment (before bun run build):
VITE_FEATURE_CONVERSATION_DB=true
VITE_AUTH_PROVIDER=clerk
VITE_CLERK_PUBLISHABLE_KEY=pk_live_...
```

```bash
## Runtime — select or force a backend:
CONVERSATION_STORE_BACKEND=postgres   # or: agentstate, d1, memory
DATABASE_URL=postgresql://user:pass@host:5432/db
```

When `CONVERSATION_STORE_BACKEND` is not set, the server auto-selects the first available backend in the order described below.

## Backend resolution order

When `CONVERSATION_STORE_BACKEND` is not set (or set to an unrecognized value), the server picks the first available backend in this order:

1. **AgentState** — when `AGENTSTATE_API_KEY` is set and `CONVERSATION_STORE_BACKEND` is not `d1`, `postgres`, or `memory`.
2. **Cloudflare D1** — when the `CONVERSATIONS_D1` binding is present (Cloudflare Workers only).
3. **Postgres** — when `DATABASE_URL` is set.
4. **Memory** — fallback in development/CI. Conversations are lost on process restart.

You can force a specific backend by setting `CONVERSATION_STORE_BACKEND` to `agentstate`, `d1`, `postgres`, or `memory`.

## AgentState self-host quickstart

AgentState is a cloud-hosted conversation store. It works on any deployment target (Cloudflare Workers, Docker, Kubernetes, Vercel).

1. Get an API key at [agentstate.app](https://agentstate.app).

2. Set at build time:

```bash
VITE_FEATURE_CONVERSATION_DB=true
VITE_AUTH_PROVIDER=clerk
VITE_CLERK_PUBLISHABLE_KEY=pk_live_...
```

3. Set at runtime:

```bash
AGENTSTATE_API_KEY=as_live_...
## CONVERSATION_STORE_BACKEND=agentstate  # optional — auto-selected when key is present
```

4. Optional: enable AI enrichment of stored conversation titles:

```bash
AGENTSTATE_AI_ENRICH=true
```

5. Deploy. No migrations needed — AgentState manages the schema.

**Cloudflare Workers** — add via `wrangler secret put`:

```bash
wrangler secret put AGENTSTATE_API_KEY
```

```toml
## wrangler.toml [vars]
CONVERSATION_STORE_BACKEND = "agentstate"
```

**Docker:**

```bash
docker run -d --name chmonitor -p 3000:3000 \
  -e AGENTSTATE_API_KEY='as_live_...' \
  -e CONVERSATION_STORE_BACKEND='agentstate' \
  ghcr.io/duyet/chmonitor:vX.Y.Z
```

**Kubernetes:**

```bash
kubectl create secret generic chmonitor-agentstate \
  --from-literal=AGENTSTATE_API_KEY='as_live_...'
```

```yaml
## in ConfigMap
CONVERSATION_STORE_BACKEND: "agentstate"
```

## Request / response flow

```mermaid
sequenceDiagram
    participant B as Browser
    participant A as /api/v1/agent
    participant S as Store backend

    B->>A: POST {message, hostId} + auth token
    A->>S: Read conversation thread
    S-->>A: Existing messages
    A->>A: Run agent (tool loop)
    A->>S: Append new messages
    S-->>A: OK
    A-->>B: Streamed response
```

On each request the server loads the thread from the store, runs the agent, then writes the updated thread back before streaming the response to the browser.

## Auth requirement

Server stores require an authenticated user identity to namespace threads. If the request is unauthenticated, the server skips persistence and the browser keeps local history. To enable server persistence, configure an auth provider — see [Authentication](/authentication).

## Deprecated alias

`NEXT_PUBLIC_FEATURE_CONVERSATION_DB=true` is accepted as an alias for `VITE_FEATURE_CONVERSATION_DB=true` but is deprecated. Do not use it for new deployments.

## Next steps

- [Store Backends](/ai-agent/conversation-history/backends) — exact config for each backend with platform recommendations
