Files
sherlock/config.example.toml
T
amacocianandCopilot d9ecb908b8
Charlie/project-charlie: Build image / config (push) Successful in 1s
Charlie/project-charlie: Build image / build (push) Skipped
Charlie/project-charlie: Deploy runners / config (push) Successful in 2s
Charlie/project-charlie: Deploy runners / deploy-morgott (push) Skipped
Charlie/project-charlie: Deploy runners / deploy-melina (push) Skipped
Charlie/project-charlie: Deploy stack / config (push) Successful in 1s
Charlie/project-charlie: Deploy stack / deploy (push) Skipped
Release / release (push) Successful in 21s
agent: support the codex, opencode and goose CLIs
Backends grow from "flag + env scrub" to four knobs — render, mcpArgs,
mcpEnv, forbidEnv — so a CLI can receive sherlock's MCP servers however
it accepts them, without ever editing the operator's own config:

  - codex:    repeated `-c mcp_servers.<name>....` TOML overrides, which
              layer over ~/.codex/config.toml. $CODEX_HOME is avoided on
              purpose: it also holds auth.json.
  - opencode: $OPENCODE_CONFIG pointing at a rendered file in opencode's
              own schema; it is one layer of that CLI's config merge.
  - goose:    repeated `--with-extension <name>:<env> <command line>`.
              Those belong to a subcommand, so MCP flags are appended
              after the operator's arguments and a bare `sherlock goose`
              defaults to `goose session`.

Verified against the real CLIs (codex 0.153.2, opencode 1.18.27, goose
1.49.0): codex registers both servers with args and env intact, opencode
and goose actually spawn them, and SHERLOCK_LOCAL_CONFIG reaches the MCP
process in every case.

Gemini and Cursor CLIs are deliberately left out: they read MCP servers
only from config files they own, so wrapping them would mean writing
into ~/.gemini, ~/.cursor or the checked-out repository.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fdf86527-ca08-420d-89ee-47bcee60bebe
2026-09-04 11:18:56 +02:00

97 lines
4.8 KiB
TOML

# sherlock conifugration - lives in $XDG_CONFIG_HOME/sherlock/config.toml
#
# A project-local .sherlock.toml (looked up from the working directory
# upwards) is layered on top of this file: sections it declares replace
# the same-named sections here, everything else falls back to this file,
# and an empty section means "disabled". See docs/configuration.md.
# ── OAuth login ───────────────────────────────────────────────────────
# Optional browser executable/name for OAuth login URLs. Leave unset to
# use the OS default browser (xdg-open/open/rundll32).
# [oauth]
# browser = "firefox"
# ── Providers ────────────────────────────────────────────────────────
# A [providers.<name>] block is a reusable OAuth/OIDC client. Services
# point at one via `provider = "<name>"`. Define a provider once and
# share it across every service it fronts.
#
# `sherlock-cli` is the Authentik provider sherlock authenticates the
# operator against for Authentik-fronted services (grafana, gssh, …).
# It is a Public client (PKCE, no secret); its redirect URI must be
# whitelisted as http://127.0.0.1:6990/callback.
[providers.sherlock-cli]
issuer = "https://id.example.com/application/o/sherlock-cli/"
client_id = "REPLACE_WITH_AUTHENTIK_SHERLOCK_CLI_CLIENT_ID"
# ── Services ─────────────────────────────────────────────────────────
# Each [services.<name>] is one MCP's target. <name> is also the wallet
# key (`sherlock logout <name>`). A service either:
# - references a shared provider: provider = "sherlock-cli"
# - or supplies its own identity: issuer + client_id (+ client_secret)
# `base_url` (the API origin the MCP calls) is always required.
# Gitea runs its OWN OAuth2 server, so it carries an inline identity
# rather than referencing the Authentik provider.
[services.gitea]
issuer = "https://gitea.example.com"
client_id = "REPLACE_WITH_GITEA_OAUTH2_APP_CLIENT_ID"
# client_secret = "" # set only if your Gitea enforces it on token exchange
base_url = "https://gitea.example.com"
# Grafana and Gssh both reuse the shared Authentik provider above.
[services.grafana]
provider = "sherlock-cli"
base_url = "https://grafana.example.com"
[services.gssh]
provider = "sherlock-cli"
base_url = "https://terminal.example.com"
[services.searxng]
provider = "sherlock-cli"
base_url = "https://search.example.com"
# ── Custom agents ────────────────────────────────────────────────────
# A [agents.<name>] block exposes an operator-owned wrapper command as
# `sherlock <name>`, reusing a built-in backend's MCP/-env conventions.
# `backend` must be one of claude, codex, copilot, goose, opencode — it
# tells sherlock how to hand that CLI its MCP servers. `command` is the
# binary to exec and defaults to <name> when omitted.
#
# Example: surface a `copilot-local` wrapper (Copilot CLI pointed at a
# self-hosted model via BYOK) as `sherlock copilot-local`.
# [agents.copilot-local]
# command = "copilot-local"
# backend = "copilot"
# ── Headless (machine) auth ──────────────────────────────────────────
# The blocks above use the interactive browser login. For unattended
# deployments (e.g. jarvis in a container) a service uses one of two
# headless modes instead. Secrets never live in this file: the *_env
# fields name environment variables sherlock reads at startup. In a
# container, also set SHERLOCK_KEYRING=memory to skip the OS keyring.
#
# Client-credentials grant: mint a bearer from username + app password,
# no browser. Keeps the account's identity/group claims in the JWT.
# [services.grafana]
# provider = "sherlock-cli"
# base_url = "https://grafana.example.com"
# grant = "client_credentials"
# username = "sys-jarvis"
# password_env = "JARVIS_GRAFANA_PASSWORD"
#
# Static token: bypass OAuth entirely with a scoped PAT (Gitea has no
# machine grant, so its API is reached this way). Needs no grant/issuer.
# [services.gitea]
# base_url = "https://gitea.example.com"
# token_env = "JARVIS_GITEA_TOKEN"
# ── Disabling ────────────────────────────────────────────────────────
# A section with no keys means disabled. Most useful in a project-local
# .sherlock.toml, where it switches off something this file enables:
#
# [services.gssh] # do not offer the gssh MCP here
# [agents.claude] # do not offer the claude agent here