Files
sherlock/docs/architecture.md
T
amacocian 801bbcec09
CI / build (push) Has been cancelled
Phase 0
2026-05-24 21:53:20 +02:00

5.2 KiB

Architecture

Condensed from ../plan.md. Read this first; jump to the plan for the longer rationale.

One-paragraph summary

Sherlock is a per-user credential broker + agent-CLI wrapper that runs on the operator's workstation. It owns a single Authentik session, exchanges it for per-service tokens on demand, injects those tokens as environment variables into thin stdio MCP servers, and then execs the agent CLI of your choice (Copilot, Claude Code, …) with the right MCP config. No long-lived service tokens live on disk in the clear, and the agent never sees a credential it isn't supposed to.

Diagram

┌────────────────────────────────────────────────────────────┐
│  user                                                      │
│    │                                                       │
│    │ `sherlock run copilot`   (or `sherlock copilot`)      │
│    ▼                                                       │
│  ┌─────────────┐  spawns, injects env  ┌──────────────┐    │
│  │ copilot CLI │ ────────────────────▶ │ gitea-mcp    │    │
│  │             │                       │ grafana-mcp  │    │
│  │             │                       │ gssh-mcp     │    │
│  └─────┬───────┘                       └──────┬───────┘    │
│        │ unix socket                          │            │
│        ▼                                      ▼            │
│  ┌──────────────────────────────────────────────────┐      │
│  │ sherlock-broker (systemd --user daemon)          │      │
│  │  - owns the Authentik OAuth state                │      │
│  │  - per-service token cache + refresh             │      │
│  │  - RFC 8693 token exchange                       │      │
│  │  - hosts loopback HTTP for browser callbacks     │      │
│  │  - exposes sherlock-mcp (stdio) for consent flow │      │
│  └────────────────────┬─────────────────────────────┘      │
│                       │ OIDC / token exchange              │
│                       ▼                                    │
│                 Authentik (id.alexandru.macocian.me)       │
└────────────────────────────────────────────────────────────┘

Components

Component Lives at Owns
sherlock (CLI) cmd/sherlock/ login, logout, status, run, agent-name aliases (copilot, claude, …). Renders the per-session MCP config, then execs the agent per its profile.
sherlock-broker (daemon) cmd/sherlock-broker/ Long-running per-user daemon. Authentik PKCE; loopback redirect; token cache (encrypted at rest); single-flight refresh; RFC 8693 token exchange; Unix-socket RPC.
internal/agent/ Agent-profile loader + spawner. Single integration point with any agent CLI; adding one = a new TOML under agents.d/, never code here. See agent-profiles.md.
internal/authn/ OIDC / PKCE / token-exchange primitives. See auth-model.md.
internal/config/ TOML loaders for ~/.config/sherlock/{agents,services}.d/.
internal/socket/ UDS RPC server (broker side) + client (CLI + MCPs).
internal/mcp/ Shared stdio-MCP helpers (used by every per-service MCP binary).
cmd/gitea-mcp/ (Phase 2) First per-service MCP. Reads GITEA_TOKEN from env, refreshes via broker socket on 401.
cmd/gssh-mcp/ (Phase 3) Thin HTTP+WS client to the existing gssh server. No local certs. See gssh-integration.md.
cmd/grafana-mcp/ (Phase 4) Grafana tools, OIDC-federated via Authentik.
cmd/sherlock-mcp/ (Phase 4) The only interactive MCP. Exposes oauth.consent(service), auth.whoami(), auth.list_services(), auth.revoke(service).

Why the broker is separate from the wrapper

  • One Authentik browser flow per workstation, shared across every terminal.
  • A stable loopback listener for non-Authentik OAuth callbacks.
  • Token refresh races are solved once, in one place.
  • The wrapper can stay agent-agnostic: it only knows "ask the broker for a token, then exec the agent". Swap Copilot for Claude Code without touching auth code.

Boundaries

  • Sherlock does not federate identities — that's Authentik's job.
  • Sherlock does not issue SSH certificates — gssh already does that internally when it authenticates a WebSocket session. See gssh-integration.md.
  • Sherlock does not know what tools an MCP exposes — that's the MCP's job. Sherlock only ensures the MCP has the credential it needs.