# Configuration

> Every setting, its default, and where it can be set.

<!-- Generated from CONFIGURATION.md by scripts/site/build.ts: edit that file instead. -->

Solipsist works with no configuration. Every setting has a default; change only what you need.

Settings come from three places. A later one wins over an earlier one:

1. the **defaults** (listed below);
2. the **config file**, `solipsist.yaml`;
3. the **environment**. The installed `solipsist` reads no `.env`: the folder it runs in may be someone else's. Run
   from source (`bun src/index.ts`), Bun loads the folder's `.env` into the environment first.

A setting that is wrong stops the server at start and says which one:

```
solipsist.yaml: agent.concurrency must be a whole number ≥ 1 (got "three")
AGENT_CONCURRENCY must be a whole number ≥ 1 (got "three")
```

## The config file

The first of these that exists is used. Files are not merged.

1. `--config <path>` on the command line, or the `SOLIPSIST_CONFIG` variable;
2. `solipsist.yaml` in the folder you start the server from;
3. `~/.config/solipsist/config.yaml`.

Relative paths inside the file are resolved against the file's own folder, so it means the same wherever you start the
server. `~` is expanded. The server prints the file it loaded when it starts. A change to the file applies at the next
start.

[`solipsist.example.yaml`](https://github.com/pulkitsharma07/solipsist/blob/main/solipsist.example.yaml) lists every key with its default. A minimal file:

```yaml
md_search_paths:
  - "*.md"
  - docs/**/*.md
server:
  port: 4000
agent:
  concurrency: 2
  cost_limit_usd: 5
```

## Secrets

`pi.openrouter_key` may go in the file, but `solipsist.yaml` is easy to commit by accident. Keep
keys in your environment (`SOLIPSIST_OPENROUTER_KEY`, in your shell's profile). The server never prints a key:
errors and `--print-config` say only whether one is set.

## Seeing what is in effect

`bun src/index.ts --print-config` prints every setting's value and where it came from (`default`, `file` or
`env:NAME`), then exits without starting a server.

In the browser, the gear on a page (and on the pages list) opens the settings dialog. Its **Loaded settings** tab shows the
same list for the running server: each value, where it came from, a filter box, and the config file that was loaded. A
secret shows only as `set`. The list comes from `GET /api/config`, which, like the prompts, answers only requests from
the machine the server runs on, even when `server.host` opens it to the network.

## Prompts

Everything the agents are told is a prompt you can change: the protocol (the system prompt), the prompt each run is sent,
one prompt per task type, and the chat prompts. Open the gear, then **Prompts**; pick one, edit it, **Save**.
**Reset to default** brings the built-in text back.

- Your edits are kept outside the repository, one markdown file per prompt, in `prompts.dir` (default
  `~/.config/solipsist/prompts`; `SOLIPSIST_PROMPTS_DIR`): `protocol.md`, `run.md`, `type.gen.md`, and so on. A prompt
  with no file uses the built-in text. You can edit or delete the files by hand; they are read each time they are used.
- A change applies to the next run and the next agent started. An agent already running or waiting keeps the
  instructions it began with.
- Some prompts are templates with `{slots}` the server fills in (the dialog lists them). A slot marked required must stay
  in the text; a save without it is refused and says why. Another `{word}` is kept as written.
- Saving text identical to the built-in one removes the file, so a prompt is "edited" only when it differs.

## Reference

The rest of this page is generated from the settings table (`src/server/config/`). To add or change a setting, edit its
row there and run
`bun scripts/config-docs.ts`; a test fails while this page or the example file is out of date.

<!-- config:start (generated by `bun scripts/config-docs.ts`; edit the rows in src/server/config/ instead) -->

### Pages

Which files Solipsist offers to work on. This key sits at the top of the file, outside any section.

#### `md_search_paths`

Glob patterns for the .md files the file dialog offers.

| Type | Default | Environment |
|---|---|---|
| a list of text (at least one) | `*.md` | `SOLIPSIST_SEARCH_PATHS` |

Relative to the folder Solipsist runs in. `*.md` is that folder only; `docs/**/*.md` goes deep; a folder name means the .md files directly in it. A brace holding a slash (`{a,b/c}/*.md`) matches nothing, so list those as separate patterns. Dot-folders and `node_modules` are never offered. Anything git ignores isn't either, unless the pattern starts with the folder's name: `docs/*.md` finds them in an ignored `docs/`, and `**/*.md` doesn't. An absolute path or `..` is an error. Change it and restart, and the file dialog opens again with the new files it finds.

### server

How the web server is reached.

#### `server.port`

The port of the server.

| Type | Default | Environment |
|---|---|---|
| a whole number from 0 to 65535 | `3000` | `PORT` |

Give each checkout or worktree its own port in `.env` when you run several servers. `0` lets the system pick one.

#### `server.host`

The address the server listens on.

| Type | Default | Environment |
|---|---|---|
| text | `localhost` | `SOLIPSIST_HOST` |

`localhost` keeps the server to this machine. `0.0.0.0` opens it to the network. There is no login, so do that only on a network you trust.

#### `server.allowed_hosts`

Names other than localhost the server answers to.

| Type | Default | Environment |
|---|---|---|
| a list of text |  | `SOLIPSIST_ALLOWED_HOSTS` |

The server refuses a request addressed to any name but `localhost`, `127.0.0.1` or `[::1]` on its own port, so a website can't reach it by pointing a name of its own at this machine. Opened to the network (`server.host`) or behind a proxy, list the names it is reached by: `notes.lan`, or `notes.lan:8080` for that port only. In the environment, a comma-separated list.

### agent

The background agents: when a run starts, how many run at once, how long they may take and cost, and how long a finished agent is kept for a chat.

#### `agent.gap_ms`

The pause before a failed run is retried (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `1000` | `AGENT_GAP_MS` |

Raise it if a failing run (a bad key, a provider outage) retries too eagerly.

#### `agent.settle_ms`

How long the page must stay unchanged before a run starts (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `2000` | `AGENT_SETTLE_MS` |

A run is sent only once you have stopped typing, so the agent reads the page you meant. Raise it if runs start while you are still writing.

#### `agent.single_agent`

One lasting agent per page handles every request, instead of a fresh agent per request.

| Type | Default | Environment |
|---|---|---|
| true or false | `false` | `AGENT_SINGLE_AGENT` |

On: the page has a single agent, which takes its requests one at a time and keeps everything it has read and written in its conversation, so each answer after the first is shorter to prompt and quicker to come back (`agent.concurrency` is then 1). It snoozes like any kept agent (`agent.wait_ms`) and takes its conversation up again on the next request. Off: each request gets a fresh agent that reads the page from nothing. Needs an engine that keeps conversations (Pi).

#### `agent.concurrency`

How many runs may work on one page at once.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `3` | `AGENT_CONCURRENCY` |

Each pending request gets a run of its own. Lower it to spend less at once; raise it for a page with many requests. `agent.max_runs` caps all pages together.

#### `agent.max_runs`

How many runs may work at once across all pages.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `6` | `AGENT_MAX_RUNS` |

A page held back by this limit is skipped as busy and tried again later.

#### `agent.run_timeout_ms`

How long one run may take before it is stopped (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `600000` | `AGENT_RUN_TIMEOUT_MS` |

A run that passes it is stopped and counts as failed.

#### `agent.cost_limit_usd`

The most the agents may cost (US dollars) before no new run starts.

| Type | Default | Environment |
|---|---|---|
| a number > 0 | unset | `AGENT_COST_LIMIT_USD` |

Counted from the server's start only (`agent.cost_limit_day_usd` and `agent.cost_limit_month_usd` span restarts), over runs and chat replies; a run in flight is not cut short. Unset: no limit.

#### `agent.cost_limit_day_usd`

The most the agents may cost (US dollars) in one day before no new run starts.

| Type | Default | Environment |
|---|---|---|
| a number ≥ 0 | `5` | `AGENT_COST_LIMIT_DAY_USD` |

The day is the local calendar day. Counted over runs and chat replies, and across restarts: the server adds up today's cost from its log when it first needs it (so it only knows what the log holds; `log.file` off means counting from start). A run in flight is not cut short. 0: no daily limit.

#### `agent.cost_limit_month_usd`

The most the agents may cost (US dollars) in one calendar month before no new run starts.

| Type | Default | Environment |
|---|---|---|
| a number ≥ 0 | `50` | `AGENT_COST_LIMIT_MONTH_USD` |

Counted like `agent.cost_limit_day_usd`, over the local calendar month. 0: no monthly limit.

#### `agent.max_unmetered_runs_day`

Runs in one day on engines that report no cost, before no new run starts on them.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `40` | `AGENT_MAX_UNMETERED_RUNS_DAY` |

The dollar limits can only bound an engine that says what a run cost. One that doesn't (`AgentEngine.reportsCost`) would run under them untouched, so it is bounded by count instead. Counted over the local calendar day and across restarts, from the server's log, like `agent.cost_limit_day_usd`; runs on engines that do report a cost are not counted. 0: no limit, and nothing bounds an engine that reports no cost.

#### `agent.wait_ms`

How long a Pi agent waits after its suggestion before it snoozes (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `300000` | `AGENT_WAIT_MS` |

While it waits, a chat or an Ask again continues at once. After it, the conversation is kept on disk and woken on demand, which is slower but holds no process.

#### `agent.max_waiting`

How many agents may wait on one page at once.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `6` | `AGENT_MAX_WAITING` |

Per page, unlike `agent.max_runs`. Past it, the oldest waiting agent snoozes.

#### `agent.max_failures`

Failed runs in a row on one request before it is declined.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `3` | `AGENT_MAX_FAILURES` |

A declined request is left to you, with the reason, instead of being retried for ever.

#### `agent.preferred_models`

The models you prefer, listed first in the picker; the first that can run is the default.

| Type | Default | Environment |
|---|---|---|
| a list of text | `pi:openrouter/~deepseek/deepseek-flash-latest` | `SOLIPSIST_PREFERRED_MODELS` |

Choices as `engine:model` — `pi:openrouter/~deepseek/deepseek-flash-latest` — or a bare model id, whichever engine has it. They are pinned to the top of the model picker under Preferred, in this order, and a page with no model of its own starts on the first whose engine can run now. The default is DeepSeek Flash on Pi; set it empty to let the engines' own order decide. One no engine has is passed over. In the environment, a comma-separated list.

### review

Page Review: the agent that reads a page and leaves questions in the margin. It has no way to write the page, so a review never changes a word of it. These settings say how many questions a review may raise and how many the page may hold.

#### `review.on`

Whether a page may be read for questions at all.

| Type | Default | Environment |
|---|---|---|
| true or false | `true` | `SOLIPSIST_REVIEW` |

Page Review is the agent that reads your page and leaves questions in the margin, and never writes the page. Off, Page review is refused and the button is hidden.

#### `review.curator`

Who scores the questions a review raised.

| Type | Default | Environment |
|---|---|---|
| one of `self` | `self` | `SOLIPSIST_REVIEW_CURATOR` |

`self` is the reviewer's own score for its own questions, which costs nothing and ranks about as well as you would expect. A second, separate model that scores them against a written rubric is the next thing to build here; until it exists, `self` is the only choice.

#### `review.min_score`

A question scoring below this (0 to 10) is dropped.

| Type | Default | Environment |
|---|---|---|
| a number ≥ 0 | `0` | `SOLIPSIST_REVIEW_MIN_SCORE` |

Raise it to see fewer, better questions. With `review.curator: self` the score is the reviewer's own, so a threshold mostly filters out what it already thought was weak.

#### `review.min_kept`

The fewest comments a page may hold on screen at once.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `4` | `SOLIPSIST_REVIEW_MIN_KEPT` |

However short the page. The cap is `max(this, round(review.kept_per_block × non-empty blocks))`.

#### `review.kept_per_block`

How many more comments a page may hold per non-empty block.

| Type | Default | Environment |
|---|---|---|
| a number ≥ 0 | `0.2` | `SOLIPSIST_REVIEW_PER_BLOCK` |

A paragraph, heading, list item or table cell with text in it counts as a block. 0.2 means one more comment for every five.

#### `review.max_per_review`

The most questions one review may raise.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `3` | `SOLIPSIST_REVIEW_MAX` |

The reviewer is asked for at most this many, and a call with more comes back for it to cut down. Low on purpose: three questions a writer reads beat ten they scroll past.

#### `review.max_gloss_words`

A margin note longer than this many words is dropped.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 2 | `10` | `SOLIPSIST_REVIEW_GLOSS_WORDS` |

The note is meant to be two to five words, the way a reader pencils one beside a passage. One that runs to a sentence did not follow the brief, which is a fair sign of the question's quality too.

### suggestions

The limits on what an agent may suggest.

#### `suggestions.max_chars`

The longest suggestion accepted from an agent (characters).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `20000` | `SOLIPSIST_MAX_SUGGESTION` |

A longer reply counts as a failed run. Raise it if you ask for long rewrites.

#### `suggestions.max_edits`

The most changes to other blocks one suggestion may carry.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `20` | `SOLIPSIST_MAX_EDITS` |

A suggestion may also change blocks of the page outside its own range (`remove this table`). `0` forbids that.

### chat

The chat with an agent.

#### `chat.max_message_chars`

The longest message you may send in a chat (characters).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `20000` | `SOLIPSIST_MAX_MESSAGE` |

A longer message is refused.

### pi

The Pi agent engine, on OpenRouter. It is the default engine.

#### `pi.models`

The models the picker lists at a glance, the first being the default.

| Type | Default | Environment |
|---|---|---|
| a list of text (at least one) | `openrouter/~deepseek/deepseek-flash-latest`, `openrouter/anthropic/claude-haiku-5.5`, `openrouter/anthropic/claude-sonnet-5.5`, `openrouter/~z-ai/glm-latest`, `openrouter/~moonshotai/kimi-latest`, `openrouter/google/gemini-3.5-flash` | `SOLIPSIST_PI_MODELS` |

Ids as `<provider>/<model>`, e.g. `openrouter/anthropic/claude-haiku-5.5`. This is only the shortlist: the picker's search offers every model your Pi has credentials for, read from Pi's own catalog. One your Pi can't run is left out, and when Pi names a default of its own (`defaultModel` in `~/.pi/agent/settings.json`) it is listed first. In the environment, a comma-separated list.

#### `pi.tools`

The tools a Pi agent may use (Pi's names).

| Type | Default | Environment |
|---|---|---|
| a list of text (at least one) | `read`, `grep`, `find`, `ls`, `bash`, `write`, `edit` | `SOLIPSIST_PI_TOOLS` |

The default leaves out MCP, codemode and tool search, so an agent starts none of your MCP servers. Bash runs where Solipsist runs and is not confined; a write guard limits `write` and `edit` to the page's image folder. In the environment, a comma-separated list.

#### `pi.openrouter_key`

Your OpenRouter API key. **A secret.**

| Type | Default | Environment |
|---|---|---|
| text | unset | `SOLIPSIST_OPENROUTER_KEY` |

Better kept in your environment than in this file, which you may commit. `OPENROUTER_API_KEY` works too, and so does any login Pi has saved of its own (`pi`, then `/login`) — for OpenRouter or any other provider, which a key set here overrides. With none of them Pi is listed but greyed out.

### prompts

Where your edits to the agents' prompts are kept. Edit them in the app (the gear on a page) or by hand; the built-in text is the default.

#### `prompts.dir`

The folder where your edited prompts are kept, one markdown file per prompt.

| Type | Default | Environment |
|---|---|---|
| a folder path | `~/.config/solipsist/prompts` | `SOLIPSIST_PROMPTS_DIR` |

A prompt with a file here uses it; one without uses the built-in text, so deleting a file brings the default back. Point it at a shared or committed folder to keep prompts with a team. Edits apply to the next run and the next agent started.

### comments

How long the hidden block at the end of a page keeps what it no longer needs.

#### `comments.orphan_keep_ms`

How long an orphaned comment or a decided suggestion is kept before it is removed (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `30000` | `ORPHAN_KEEP_MS` |

An orphan is a comment whose text is gone. The delay lets an undo bring it back.

### support

The strip that asks you to buy a licence once you have used Solipsist for a while. Nothing is locked and nothing is sent anywhere: the count stays on this machine.

#### `support.after_days`

After how many days of writing the page asks you to buy a licence.

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 0 | `14` | `SOLIPSIST_SUPPORT_AFTER_DAYS` |

A day counts when you save a page on it. "Later" asks again 7 such days on; "I've bought one" stops it. `0` never asks.

#### `support.dir`

The folder of `support.json`, which keeps that count and your answer.

| Type | Default | Environment |
|---|---|---|
| a folder path | `~/.config/solipsist` | `SOLIPSIST_SUPPORT_DIR` |

Delete the file to start the count again.

### log

What the server records about itself, and how it shows the agents in a terminal.

#### `log.file`

The server's log of its core actions, one JSON line each: a file path, or `off`.

| Type | Default | Environment |
|---|---|---|
| a file path, or "off" | unset | `SOLIPSIST_LOG` |

Saves, refused saves, outside edits, runs, waits, chats, stops and errors. Default: `.solipsist/server.log` in the folder Solipsist runs in. Always off under `bun test`.

#### `log.max_bytes`

The size at which the log is moved aside (bytes).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1024 | `10485760` | `SOLIPSIST_LOG_MAX_BYTES` |

Past it, the file moves to `server.log.1` (the one before is dropped) and a new one starts, so the log never grows for ever.

#### `log.tui`

Whether the server shows the agents in a terminal view when it runs in a terminal.

| Type | Default | Environment |
|---|---|---|
| true or false | `true` | `SOLIPSIST_TUI` |

`false` gives the plain line output instead, which is easier to read in a CI log or a pipe.

### dev

For working on Solipsist itself.

#### `dev.mock_comments`

Add a mock comment to every watched page every dev.mock_interval_ms.

| Type | Default | Environment |
|---|---|---|
| true or false | `false` | `MOCK_COMMENTS` |

For trying the comment margin without an agent.

#### `dev.mock_interval_ms`

The interval of the mock comments (ms).

| Type | Default | Environment |
|---|---|---|
| a whole number ≥ 1 | `5000` | `MOCK_INTERVAL_MS` |

Only used with dev.mock_comments.

### Variables that are not settings

These are read as plain environment variables, outside the settings table.

| Variable | Why |
|---|---|
| `SOLIPSIST_CONFIG` | The path of the config file, the same as `--config <path>` on the command line. |
| `NODE_ENV` | Bun's and every tool's. `development` (`bun run dev`) turns on hot reload and the dev routes, `test` (`bun test`) the dev routes and the test stand-in; anything else, or unset, is neither. Set by a `.env` in the folder Solipsist runs in, it counts as unset. |
| `AGENT_CMD` | The test fake: a JSON argv that stands in for an engine. Read only when `NODE_ENV` is `test`, and never when a `.env` in the folder sets it. Never a user setting. |
| `PI_CODING_AGENT_DIR` | Pi's own: where it keeps its configuration. |
| `OPENROUTER_API_KEY` | Pi reads it itself. A fallback behind `pi.openrouter_key`. |
| `FUZZ_RUNS` | Test runner only (`test/fuzz.test.ts`). |
| `E2E_CHANNEL` | Test runner only (the browser the UI tests use). |
| `E2E_VIDEO` | Test runner only (where the UI tests record videos). |

<!-- config:end -->

Source: https://solipsist.sh/docs/reference/configuration/
