Skip to content

Configuration schema

The canonical JSON Schema is published at:

https://harnery.com/schemas/config.schema.json

Reference it in your config file’s $schema field to get editor autocomplete and validation in VS Code, Cursor, Zed, JetBrains, and anywhere else that respects $schema.

Settings resolve from these sources, lowest precedence first (later wins):

  1. ~/.config/harnery/config.jsonc: the user-global file, a base for every project on the machine (honors XDG_CONFIG_HOME; falls back to ~/.config)
  2. <project>/.harnery/config.jsonc: the project settings file, which overrides the user-global file field by field (a project that sets only web.port keeps a user-global web.bind)
  3. CLI flags + environment variables: per-invocation overrides (see each field’s env note)

The config reader (src/core/config.ts) merges the two files and owns binName, hooksSetupHint, hooks, agents, instructions, tools, workflow, skills, presence, coord, logs, artifacts, backup, and sync; the web layer reads files. Four values are project-only. The binName pin that harn init guards against re-stamping ignores the user file. So do agents.finalizationRoots, instructions.promptReminder, and hooks.promptContext. A global setting cannot grant filesystem authority, execute a project extension, or carry one project’s wording into another. A user-global binName is a display fallback, never a project pin.

Field Type Default Description
$schema string none URL to the JSON Schema (used by editors for autocomplete/validation)
project_name string repo basename Display name surfaced in log lines and to peers
binName string harn Host CLI bin name used in agent-facing prompts (see below)
agents object see below Host-owned agent ritual policy
coord object see below Coord-layer settings
artifacts object see below Working-artifact retention
logs object see below Soft storage budgets for manifest-backed logs
tools object see below Managed-tool provisioning consent (tools.ripgrep.autoInstall)
workflow object see below Workflow-engine defaults
skills object see below Shipped-skill suppression (skills.exclude)
hooks object see below Optional project-owned hook extensions
instructions object see below Host-owned agent instructions and prompt reminder
presence object see below Cross-machine presence transports
files object see below Web file-viewer path policy (additive deny/allow globs)
web object see below Standalone Next.js dashboard
backup object see below restic-backed snapshots
sync object see below rclone-backed cross-machine sync

The host CLI’s bin name. Harnery’s coord binaries (agent-hook, agent-coord) and the web UI run as Harnery itself, so they can’t see the name of a consumer CLI that composes Harnery (e.g. myapp). They read it back from this field for every agent-facing string: end-of-turn nudges, council prompts, worktree-conflict hints. harn init stamps it automatically for a consumer (any bin name other than the harn default). Resolution precedence: HARNERY_BIN env → this field → "harn".

Field Type Default Description
requireGitFinalization bool false Require the guarded Git check at the end of tool-using turns
finalizationRoots object[] [] Extra project-owned roots with a git or output end-turn disposition

When enabled, automatic prompts and Stop remediation request agents status --end-turn instead of plain agents status. The Stop verdict also requires evidence that the guarded command ran; an ordinary status call does not satisfy the opted-in policy. The --end-turn capability remains available for manual use when this setting is false.

{
"agents": {
"requireGitFinalization": true,
"finalizationRoots": [
{ "path": "../another-repository", "disposition": "git" },
{ "path": "../generated-exports", "disposition": "output" }
]
}
}

The coordination repository is an implicit git root. Every extra root comes from the project config; the user-global config cannot grant filesystem authority. Relative paths resolve from the coordination root. A git path must be the repository top level. An output path explicitly approves non-Git files and fails closed if it overlaps a Git root or the claimed path belongs to a Git repository.

Env override per process: HARNERY_AGENTS_REQUIRE_GIT_FINALIZATION=1|0.

Field Type Default Description
freshness_seconds number 600 Heartbeat age (seconds) above which the sweeper prunes an agent (10 min)
finalization object see below Multi-signal V3 session-finalization grace periods
run_quality object { "mode": "off" } Report-only run-quality observation (see below)

Resolution: HARNERY_AGENT_COORD_FRESHNESS env → this config.jsonc key → 600.

These bounds control the canonical V3 reconciler. Archive and policy signals are provisional until their grace period expires. Any new session activity cancels a pending request.

Field Default Purpose
archive_grace_seconds 600 Delay before a verified archive may end a session
idle_observe_seconds 259200 Inactivity before Harnery creates an idle observation (3 days)
idle_finalize_seconds 604800 Inactivity before idle policy finalization (7 days)
cascade_grace_seconds 3600 Delay for parent, stale, superseded, and host-loss cascades
reconcile_interval_seconds 900 Default interval for agents reconcile --watch

All values are positive integers. idle_finalize_seconds is clamped to at least idle_observe_seconds. See ADR 0077.

The run-quality evaluator derives advisory unknown, healthy, attention, or critical snapshots from bounded coordination evidence. It never blocks, steers, pauses, or stops an agent. shadow writes snapshots and transition telemetry without exposing severity; report also adds a quality line and JSON field to harn agents status. The package default is off.

Field Default Valid range
mode off off | shadow | report
evaluation_interval_seconds 30 5–3,600
snapshot_ttl_seconds 120 10–86,400
max_tail_bytes 2097152 65,536–67,108,864
evaluation_timeout_seconds 30 1–30
lock_stale_seconds 60 5–3,600
supervised_roots_per_sweep 8 1–100
thresholds.repeated_tool_calls 8 2–10,000
thresholds.consecutive_failures 5 1–10,000
thresholds.context_growth_per_minute 60000 1–10,000,000
thresholds.compaction_grace_seconds 300 0–86,400
thresholds.no_progress_evaluations 2 1–10,000

All supplied bounds must be finite integers. A malformed object disables the whole evaluator for that config digest and emits one bounded health.run_quality_config_invalid event; Harnery does not partially apply it. The snapshot TTL must be at least the evaluation interval, and the stale-lock bound must be greater than the evaluation timeout so a live writer cannot be stolen inside its allowed runtime. Snapshots live under .harnery/guard/, use a cursor separate from the session projector, and contain hashes and aggregates rather than tool bodies. See ADR 0075 for the signal, wait, freshness, and rollout contract.

Field Type Default Description
default_retention_days number 3 Retention used by harn artifacts create when neither --days nor --minutes is supplied
auto_clean boolean true Hourly-throttled cleanup at session start and before new artifact or managed QA work (same guarded deletion as artifacts clean --yes)
max_bytes integer 21474836480 Soft managed-store budget; valid range 64 MiB through 1 TiB
max_unit_bytes integer 1073741824 Per-workspace ceiling unless creation records --big; valid range 16 MiB through 1 TiB

Resolution: HARNERY_ARTIFACT_RETENTION_DAYS env → this config.jsonc key → 3. See harn artifacts for cleanup behavior.

Field Type Default Description
rotate_active_bytes integer 33554432 Active V3 byte threshold for starting a fresh epoch; non-positive disables rotation
archive_max_bytes integer 1073741824 Soft byte budget for complete closed epochs; valid range 64 MiB through 1 TiB
archive_max_age_days integer 7 Maximum closed-epoch age outside the newest minimum; valid range 1 through 3,650
archive_keep_min integer 2 Newest complete epochs protected regardless of age or bytes; valid range 1 through 1,000
archive_auto_clean boolean true Daily session-start execution of the same guarded plan as ledger-v3 archives clean --yes

Byte and age pressure never removes the active V3 directory, a protected newest epoch, an unknown directory, a symlink, or an unreadable tree. Invalid archive settings fall back to the built-in safe defaults.

logs.storage controls soft retention budgets for manifest-backed operational and debug logs. It never changes roots, formats, record limits, privacy rules, or writer behavior. It also never turns storage pressure into rejected writes.

{
"logs": {
"storage": {
"classes": {
"operational-log": {
"max_bytes": 134217728,
"max_age_days": 30
},
"debug-log": {
"max_bytes": 67108864,
"max_age_days": 7
}
},
"families": {
"web-performance-log": {
"max_bytes": 67108864,
"max_age_days": 14
}
}
}
}
}
Field Type Valid range Meaning
classes.operational-log.max_bytes integer 10 MiB through 1 TiB Managed-partition byte budget for every operational log unless a family override is more specific
classes.operational-log.max_age_days integer 1 through 3,650 Maximum sealed-segment age for every operational log unless a family override is more specific
classes.debug-log.max_bytes integer 10 MiB through 1 TiB Managed-partition byte budget for every debug log unless a family override is more specific
classes.debug-log.max_age_days integer 1 through 3,650 Maximum sealed-segment age for every debug log unless a family override is more specific
families.<family-id>.max_bytes integer 10 MiB through 1 TiB Exact-family byte budget
families.<family-id>.max_age_days integer 1 through 3,650 Exact-family sealed-segment age

Resolution starts with the source-owned family default, applies the merged class value, then applies the merged exact-family value. The project file wins over the user-global file only at the same JSON path. A family value is always more specific than a class value, even when the family value came from the user-global file.

There is no environment-variable or mutating CLI override. Invalid values, unknown classes, and unknown project family identifiers disable log-retention planning and execution. An unknown family in the user-global file is reported as dormant because it may belong to another host project.

max_bytes counts known regular files in the managed structured-log partition. Compatibility and legacy files are reported separately as unmanaged usage and are never deletion candidates. The budget is soft: an active segment or unmanaged files can leave a family over budget after all eligible sealed segments are gone. See harn logs for the evidence fields and harn storage for manual cleanup.

Field Type Default Description
subscriptionOnly bool false (engine) / true (stamped by harn init) Pin workflow run to subscription billing: API-key vars are scrubbed from every child env, so children can only authenticate via the adapter CLI’s stored login

harn init stamps { "subscriptionOnly": true } into any project without a committed workflow key; an existing workflow key of any shape is never touched (set false for key-only CI hosts). Env override per process: HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0. See run → billing safeguards.

Field Type Default Description
ripgrep.autoInstall bool false Let harn grep download the pinned, checksum-verified ripgrep into the harnery tools dir on first miss (falls back to GNU grep on any failure). A repo commits this once and every clone self-heals

Env override per process: HARNERY_TOOLS_AUTOINSTALL=1|0.

Field Type Default Description
exclude string[] [] Shipped Harnery skills this repo suppresses. The installed set is harn-decide, harn-council, harn-end, and harn-team; the injected AGENTS.md block uses the matching CLI command instead of an excluded skill
Field Type Default Description
promptContext.enabled bool false Run the project prompt-context extension from the normalized prompt hook
promptContext.timeoutMs int 15000 Whole-provider deadline, from 1 to 120,000 milliseconds
promptContext.maxOutputBytes int 65536 Maximum provider stdout, from 1 byte to 4 MiB

promptContext is project-only. Harnery ignores the user-global value because it authorizes an executable in the repository. Enabling it expects this file:

scripts/hooks/harness/extensions/prompt-context

The executable receives one harnery.prompt-context-request/v1 JSON document on standard input and returns one harnery.prompt-context-result/v1 document on standard output. Harnery validates the full response and fails open on a missing executable, timeout, signal, nonzero exit, malformed result, oversized output, or empty result. Provider stdout and stderr are never streamed into the hook protocol or copied into operational logs.

{
"hooks": {
"promptContext": {
"enabled": true,
"timeoutMs": 15000,
"maxOutputBytes": 65536
}
}
}

Claude Code, Codex, and Cursor receive validated context directly through the prompt hook.

Field Type Default Description
hostAddendumFile string none Repo-relative path to a markdown file holding this project’s own coordination policy. init splices its contents into a second managed region in AGENTS.md, init --check reports drift against it, and deinit (or deleting this key) removes the region. Harnery never parses the content
promptReminder string none One project-owned line emitted on every supported prompt for adapters that lack native reminder behavior

The path must be repo-relative and resolve inside the project, and the file must exist and be non-empty; anything else fails the run before the first write. Delete the key and re-run init to take the region back out.

promptReminder is project-only. Harnery ignores a value from the user-global config so one host’s wording cannot follow the user into another project. The line must contain 1 to 500 characters and cannot contain a newline. Harnery reads it at emission time, passes only an enable flag through the hook process, and does not copy the text into coordination state.

Harnery emits this line through Codex and Cursor UserPromptSubmit. Claude Code already supplies native output-style reminders, so Harnery does not duplicate them.

{
"instructions": {
"hostAddendumFile": ".agents/host-instructions.md",
"promptReminder": "Lead with the answer and preserve exact facts."
}
}
Field Type Default Description
enabled bool true Cross-machine presence over git refs (refs/harnery/presence/<machine> on origin). On by default when an origin remote exists; every operation is fail-silent
relay string unset Optional live upgrade: a wss:// relay URL (public reference: wss://relay.harnery.com; self-host via harn relay serve or relay/worker/). When set, hooks keep a per-machine daemon connected for seconds-latency presence; git refs stay on as the floor

Env overrides per process: HARNERY_PRESENCE=1|0 (whole feature), HARNERY_PRESENCE_RELAY=<url|0> (relay only), HARNERY_PRESENCE_FETCH_INTERVAL=<secs> (git-refs fetch throttle). A committed .harnery/presence-salt file rotates the relay room (it is NOT a secret; see decision record 0016). Full behavior: harn presence.

Field Type Default Description
port number 4276 Listen port for harn web up; 4276 spells HARN on a phone keypad
bind string 127.0.0.1 Bind address. Localhost-only by default

Precedence is --port, then HARNERY_WEB_PORT, then this config value, then 4276. An occupied port fails with web_port_in_use; Harnery never silently chooses a different URL.

Field Type Default Description
repo string ~/.cache/harnery/restic-repo restic repository path or URL
password_file string ~/.config/harnery/restic-password File containing the restic repo password
keep_daily number 7 Daily snapshots to keep on harn backup prune
keep_weekly number 4 Weekly snapshots to keep
keep_monthly number 6 Monthly snapshots to keep

Env overrides: HARNERY_RESTIC_REPO, HARNERY_RESTIC_PASSWORD_FILE.

Field Type Default Description
remote string none rclone remote name (e.g. gdrive)
prefix string harnery Subpath under the remote root

Env overrides: HARNERY_SYNC_REMOTE, HARNERY_SYNC_PREFIX.

Sync subset is fixed: identities/, journal/archived/, councils/. Heartbeats and event logs are intentionally machine-local and won’t sync.

Web file-viewer path-serving policy (the universal file viewer in harn web). The shipped secret denylist is a non-removable floor baked into code; these knobs are additive only: they extend the floor, never shrink it.

Field Type Default Description
deny_globs string[] [] Extra **/-anchored deny patterns on top of the shipped floor (treated as hard-tier: never overridable)
allow_overrides string[] [] Rescue globs consulted only when a deny came from a soft-tier floor pattern; naming a hard secret family is dropped at load

Most sections above use both config files, merged project-over-user. The agents.finalizationRoots trust boundary comes only from project config. Env vars and CLI flags override other config values per invocation:

Section Config keys Env override Other
coord freshness_seconds HARNERY_AGENT_COORD_FRESHNESS (none)
logs storage.classes, storage.families (none) exact-family values override class values; invalid retention config fails closed
artifacts default_retention_days HARNERY_ARTIFACT_RETENTION_DAYS artifacts create --days
artifacts auto_clean HARNERY_ARTIFACT_AUTO_CLEAN
backup repo, password_file, keep_* HARNERY_RESTIC_REPO, HARNERY_RESTIC_PASSWORD_FILE --keep-* flags override the prune policy per run
sync remote, prefix HARNERY_SYNC_REMOTE, HARNERY_SYNC_PREFIX ~/.config/harnery/sync.json (written by harn sync init) is a lower-precedence fallback than the sync config section
files deny_globs, allow_overrides (none) read by the web layer, not the core reader
agents requireGitFinalization, finalizationRoots HARNERY_AGENTS_REQUIRE_GIT_FINALIZATION finalizationRoots is project-only and has no env override

Full precedence for a sync remote, highest first: HARNERY_SYNC_REMOTE env → project sync.remote → user-global sync.remote~/.config/harnery/sync.json.

{
"$schema": "https://harnery.com/schemas/config.schema.json",
"project_name": "my-monorepo",
"binName": "mycli",
"coord": {
"freshness_seconds": 900
},
"artifacts": {
"default_retention_days": 3
},
"web": {
"port": 4276
},
"tools": {
"ripgrep": { "autoInstall": true }
},
"workflow": {
"subscriptionOnly": true
},
"presence": {
"relay": "wss://relay.harnery.com"
},
"sync": {
"remote": "gdrive",
"prefix": "harnery/my-monorepo"
}
}

The schema lives at schemas/config.schema.json in the Harnery repo. v1 covers the fields above; future fields land additively with semver minor bumps. Breaking schema changes require a major bump and a migration note.