Matapan docs · Operations

Operations

Day-to-day running of matapand and the matapan CLI.

Daemon management

Inspection and repair

Garbage collection

Backup and upgrade

Failure modes

Container deployment

matapand ships as an image (scripts/docker-build.sh matapan:local). One container per agent model, each with its own config, database, keys, and workspace root.

The three path-parity requirements

  1. Workspace root. matapand bind-mounts a workspace's directory into run containers by its absolute path. The workspace root mount inside the matapand container MUST be the same absolute path as the host path (e.g. -v /data/ws-chatgpt:/data/ws-chatgpt). Otherwise nested run containers get a path that does not exist on the host.
  2. Source repos. Git workspaces are linked worktrees of host repos: git worktree add writes administrative pointers containing absolute paths. Source roots must be mounted at identical absolute paths inside and outside (e.g. -v /Users/me/dev:/Users/me/dev).
  3. docker.sock. matapand needs /var/run/docker.sock to run agent workloads — mount it into the matapand container ONLY. Agent run containers are refused any docker.sock mount by spec validation. The socket is full root on the host: matapand is trusted control-plane code; treat its host access accordingly.

Config and secrets provisioning

Mount the model's config JSON at /etc/matapan/config.json (the image's default --config). Bootstrap keys and the admin obol once per model:

docker compose run --rm --no-deps --entrypoint matapan matapan-chatgpt \
  config init --home /data

(The image's entrypoint is matapand, so --entrypoint matapan is required to run the CLI. /data is the per-model volume; config init writes config.json, obol.key, secret.key, and the db, then prints the admin obol ONCE. Point --config at /data/config.json afterwards, or mount that file at /etc/matapan/config.json.) Override any setting per-instance with the MATAPAN_* env vars — env > file > defaults.

User model

The image runs as root (see the Dockerfile comment): the docker group gid varies by host and Docker Desktop manages socket access differently, so a fixed non-root uid cannot open the socket out-of-the-box. On a rootless or group-managed host you can instead run:

docker run --user "$(id -u):$(getent group docker | cut -d: -f3)" \
  -v "$XDG_RUNTIME_DIR/docker.sock:/var/run/docker.sock" ...

Runtime user and workspace ownership (B-01)

Native Linux bind mounts preserve host ownership, so the container user must MATCH the owner of workspace content (0750/0640). The daemon resolves one runtime identity at startup:

matapan doctor reports the resolved identity under runtime user and, in root mode, fails loudly if the chown probe fails (rootless daemon, read-only fs). Modes never change — only ownership.

Compose layout

See the per-model deployment folder (two services: one per model). Each service mounts its config, its data dir, its workspace dir at the identical absolute path, the docker socket, and the host dev tree at its identical absolute path; ports are published on 127.0.0.1 only.

OAuth authorization-server mode

auth.mode: oauth turns the instance into an OAuth 2.0 authorization server for connectors that require OAuth (ChatGPT, Claude).

Config (auth section):

Field Meaning
oauth_issuer Public base URL (REQUIRED, https — the tunnel hostname). Metadata endpoints and JWT iss derive from it.
oauth_redirects Redirect-URI allowlist (REQUIRED). Must cover ChatGPT's and Claude's callbacks.
oauth_client_id Public client_id (default chatgpt-mcp; printable ASCII, no spaces; override via MATAPAN_OAUTH_CLIENT_ID).
oauth_principal Principal tokens map to (default agent-<oauth_client_id>; must exist and stay within the agent scope set at startup).
oauth_allow_admin Escape hatch: permit oauth_principal to name an admin-capable principal (default false — refused). Suspends the connector authority boundary; operator use only.
oauth_owner_password_hash SHA-256 hex of the approval-gate password. Prefer the env var.

Owner password: set MATAPAN_OAUTH_OWNER_PASSWORD in the daemon's environment (compose: .env). It is hashed in memory at startup and never stored; the daemon REFUSES to start in oauth mode without it (or the config hash). The approval form cannot be bypassed — every authorization code is issued only after the owner types it.

Flow: connector discovers /.well-known/oauth-protected-resource/oauth/authorize renders the approval form (exact effective scopes + token principal shown) → owner password → code → /oauth/token (PKCE) → 24h HS256 JWT. /mcp accepts both JWTs and obols. No refresh tokens; connectors re-authorize daily. Per-client and global rate limits cover /oauth/token and the approval POST.

Token authority: tokens map to agent-<oauth_client_id> (auto-provisioned with AgentScopes — no workspace.grant, no proposal.apply), NOT admin. The connector can't grant egress/secrets and can't apply proposals; apply is human-only (CLI or a proposal.apply-scoped principal). Grant workspaces to the connector at create: oauth mode auto-grants agent-<client_id>; add more with matapan workspace create --grant <principal> (repeatable). Self-approval prevention defaults ON in oauth and charon modes.

Per-model setup: docker compose run --rm --no-deps matapan-chatgpt \ matapan config oauth-setup --config /data/config.json prints the exact connector instructions.