# MOPAC harness configuration, v0 (2026-08-28). # # Copy to harness.toml and adjust. harness.toml is gitignored: secrets never # live in this file, only refs (env:NAME | file:PATH | literal:VALUE | # mpk:PLACEHOLDER via [keyproxy]; the bw: bitwarden ref lands with the key # wrapper in build phase 3). # Vertical / stack identity for this harness instance. vertical = "demo" # Where the bash tool executes and REPORTs land (relative to the CWD the # harness is started from). work_root = "." report_dir = "reports" [loop] # Bounded turn: max LLM round trips per task (tool calls included). max_rounds = 8 # `harness loop` daemon: Redmine scan interval and state location # (append-only loop.jsonl, dedup by issue id + updated_on). poll_interval_secs = 120 state_dir = "state/loop" [redmine] url = "https://rm.example.org" key_ref = "env:HARNESS_REDMINE_KEY" # Released scope: raw /issues.json filter params... scope_query = "project=mopac&status_id=released&limit=25" # ...or a saved custom query id (scope_query wins when both are set). # scope_query_id = 42 # Custom field carrying the TASK class; issues without it get default_class. class_field = "Class" default_class = "primary" # `harness loop` status transitions: after a REPORT is noted back on the # issue, an issue whose CURRENT status matches a key here is moved to the # value. Statuses are names (resolved via /issue_statuses.json); an empty # map leaves status alone. [redmine.status_map] "In Progress" = "Done" [litellm] base_url = "http://192.168.3.78:4001" key_ref = "env:HARNESS_LITELLM_KEY" timeout_secs = 120 max_retries = 2 # KEYPROXY (optional): resolve `mpk:` key refs through the ukrrs/mopac-keyproxy # hop (POST /v1/resolve, bearer auth). Without this section, env:/file:/ # literal: refs keep working as-is — the loop runs with or without keyproxy up. # [keyproxy] # url = "http://127.0.0.1:8082" # token_ref = "env:HARNESS_KEYPROXY_TOKEN" # local ref only (no mpk: recursion) # cache_ttl_secs = 60 # GITEA (optional, off by default): `harness loop` commits each REPORT file # to this repo right after writing it (contents API, create-or-update). # [gitea] # url = "https://git.example.org" # key_ref = "env:HARNESS_GITEA_KEY" # owner = "ukrrs" # repo = "MOPAC-reports" # branch = "main" # empty = repo default branch # commit_reports = false # MODEL ROUTING v0 (static, config-only, no heuristics): [models] is the # tier map (tier alias -> concrete proxy model); [models.classes] maps task # classes to tiers. Requests go out with the CONCRETE model name resolved # here. Swapping models (e.g. glm-4.7-flash -> glm-5.3-flash once it is # configured on the proxy) is a one-line edit in this file. [models] mopac-study = "glm-4.7-flash" # flash tier mopac-code = "glm-5.2" # flagship mopac-review = "glm-5-turbo" # mid mopac-primary = "glm-5.3" # default / flagship+ mopac-vision = "glm-4.6v" # vision when needed default_tier = "mopac-primary" [models.classes] study = "mopac-study" read = "mopac-study" code = "mopac-code" architecture = "mopac-code" review = "mopac-review" summarize = "mopac-review" writeback = "mopac-review" vision = "mopac-vision" primary = "mopac-primary" # Exec tool: allow-listed bash. Org preset: deny-first, read-leaning allow # list, no sudo/ssh/network exfil. Compound commands are checked segment by # segment; command substitution and subshells are always denied. [tools.bash] enabled = true timeout_secs = 60 max_output_bytes = 100000 default = "deny" deny = [ "sudo *", "ssh *", "scp *", "nc *", "curl *", "wget *", "rm -rf *", ] allow = [ "pwd", "ls *", "cat *", "head *", "tail *", "grep *", "find *", "wc *", "echo *", "env", "git status", "git diff *", "git log *", "git show *", "go version", "go build *", "go vet *", "go test *", ] # MVP demo (Charles, 2026-08-28 ~19:00): `harness once --demo` runs this # issue through LiteLLM; the GLM self-description lands as the REPORT. [demo] id = "demo-1" subject = "MVP demo: GLM self-description" prompt = "tell me about yourself" class = "primary" # EVENTS: the `harness events` webhook receiver (Redmine/Discourse/Gitea # punch the harness; DESIGN "Events are V1 scope"). Secrets are refs only, # resolved at startup, never logged. At least one secret_ref must resolve # or the receiver refuses to start. [events] listen = ":4100" # bind address; publish on the LAN via docker -p state_dir = "state/events" # append-only events.jsonl + dedup index [events.redmine] secret_ref = "env:HARNESS_REDMINE_WEBHOOK_SECRET" # secret_header = "X-Redmine-Webhook-Secret" # default; match your plugin [events.discourse] secret_ref = "env:HARNESS_DISCOURSE_WEBHOOK_SECRET" # secret_header = "X-Discourse-Webhook-Secret" # default [events.gitea] secret_ref = "env:HARNESS_GITEA_WEBHOOK_SECRET" # Gitea always verifies via HMAC-SHA256 in X-Gitea-Signature; the # secret_header override does not apply to it. # SERVE (optional): the `harness serve` OpenAI-compatible front door for # OpenWebUI (DESIGN "OWUI front door"). Each [models.classes] class is # exposed as a servable model named mopac- (mopac-study, # mopac-code, ...); POST /v1/chat/completions runs ONE bounded stateless # conductor turn over the conversation history OWUI sends (tools off, # v0; non-streaming). Bearer vkey auth — the same value goes into the # OWUI connection config. Own port: coexists with [events]. [serve] listen = ":8090" # publish on the LAN via docker -p vkey_ref = "env:HARNESS_SERVE_VKEY" # enabled_models = ["mopac-primary", "mopac-study"] # optional subset