Configuration

Harnez keeps credentials and global settings in your user config directory.

~/.config/harnez/auth.json
~/.config/harnez/settings.json

$XDG_CONFIG_HOME/harnez is used instead when that variable is set.

These directories were named harness before the project was renamed. An install that still has the old name keeps working: each location is read from its pre-rename spelling whenever the new one is absent, and writes go back to whichever file was found. Nothing needs to be moved by hand, though renaming harness to harnez yourself is harmless.

auth.json

This file maps provider IDs to credentials. A credential is either an OAuth token set or an API key. Harnez writes the file with 0o600 permissions, and /login updates it. Never commit this file.

settings.json

{
  "providers": {
    "ollama": {
      "type": "openai-compatible",
      "baseUrl": "http://localhost:11434/v1",
      "auth": "none",
      "models": ["qwen3-coder:30b", "gpt-oss:20b"]
    },
    "company-llm": {
      "type": "openai-compatible",
      "baseUrl": "https://llm.example.com/v1",
      "auth": "api-key",
      "models": ["company-coder", "company-chat"]
    }
  },
  "model": {
    "provider": "ollama",
    "model": "qwen3-coder:30b",
    "thinkingLevel": "medium"
  },
  "fastCycle": [
    { "provider": "openai-codex", "model": "gpt-5.1-codex", "thinkingLevel": "medium" },
    { "provider": "anthropic", "model": "claude-opus-4-5", "thinkingLevel": "high" }
  ],
  "disableThinkingBlocks": false,
  "disabledMcpServers": ["spokenly"],
  "session": {
    "title": {
      "generated": true,
      "source": "keywords/yake"
    }
  }
}

providers defines named OpenAI-compatible endpoints. Provider IDs appear in /login, /model, and the model header. Each provider requires an absolute HTTP(S) baseUrl, auth set to none or api-key, and a non-empty list of unique, non-empty model IDs. IDs cannot collide with built-in providers or the legacy openai-compatible provider. Invalid definitions report the settings file and provider ID before a model request starts.

Use /login company-llm to store an API key for an api-key provider. Keys are stored only in auth.json under that provider ID, with its existing 0o600 permissions; do not put keys in settings.json. An auth: "none" provider is immediately available. Harnez supplies its OpenAI client a fixed placeholder key, so compatible local servers must tolerate an Authorization: Bearer unused header.

model is whatever /model last set. thinkingLevel is the last level selected with Shift+Tab; supported levels depend on the model. baseUrl only applies to the openai-compatible provider.

fastCycle is the list Ctrl+P steps through, in the order /fast-cycle listed the models. Each entry carries its own thinkingLevel, so Shift+Tab changes the level of the active model only, and selecting that model again restores it. Entries whose model is unavailable are skipped while cycling.

The picker writes the whole list, so an entry whose model is missing from it — for example while that provider is signed out — is dropped when you save. Cycling never drops entries; it only skips the ones it cannot resolve.

disabledMcpServers is what /mcp switched off, by server name. It lists exclusions rather than inclusions, so a server added to mcp.json later starts out connected. It follows the same layering as every other setting: the list lands in the project's .harnez/settings.json when that file exists, and in the global file otherwise — where, because the entries are plain server names, it applies to every workspace that has a server by that name.

New sessions are titled from their first prompt by default. Set session.title.generated to false to disable this. session.title.source supports:

  • keywords/yake: local, deterministic keyword extraction
  • model/<provider>/<model>:<thinking-level>: a configured model, for example model/openai-codex/gpt-5.1-codex:low.

The thinking suffix is optional. If the provider is unavailable or returns no usable title, Harnez keeps a cleaned excerpt of the first prompt. See Sessions for details about title generation and persistence.

Project overrides

A project-local .harnez/settings.json takes precedence over the corresponding global values for that project. Nested session-title fields are merged independently, so a project may override only generated or only source:

<repo>/
  .harnez/
    settings.json      # overrides ~/.config/harnez/settings.json
    harnez.sqlite     # session, event, and context storage
    skills/              # project-local skills, alongside .agents/skills
    prompts/             # project-local prompt templates, alongside .agents/prompts

Provider maps merge by provider ID. A project provider with the same ID replaces that complete global definition. Restart the server after editing either settings file; settings are not reloaded while it runs.

The legacy one-off endpoint command remains available:

/model openai-compatible <model> <base-url>

User-level skills live in ~/.harnez/skills and ~/.agents/skills, and user-level prompt templates in ~/.harnez/prompts and ~/.agents/prompts. The pre-rename .harness directories are still scanned, ranking just below their .harnez counterparts.

Environment variables

VariableEffect
HARNEZ_PORTServer listen port. Default 7432.
HARNEZ_URLServer URL the TUI connects to. Default http://localhost:7432.
HARNEZ_CONTEXT_BUDGETOverrides the context token budget used for compaction.
HARNEZ_LOG_LEVELPino log level. Default info.
HARNEZ_SHOW_STATUSSet to 1 to show status and token-usage rows in the TUI transcript.
XDG_CONFIG_HOMEOverrides the base config directory in place of ~/.config.
HARNEZ_OPENAI_API_KEY / OPENAI_API_KEYAPI key for the openai-compatible provider, checked in that order.