matapan
Cấp quyền truy cập API có phạm vi cho Chatgpt hoặc Claude vào repo của bạn thông qua một container docker. Cách ly theo mặc định, các thay đổi không ảnh hưởng đến repo của bạn trừ khi bạn cho phép.
Tài liệu
Matapan
Matapan is a local-first transactional control plane for agent-produced code. Coding agents (ChatGPT, Claude, Codex) get disposable, container-isolated workspaces and return work only as immutable, evidence-bearing proposals, applied via compare-and-swap. Your active checkout is never in the agent's write path.
The loop:
import trusted source → disposable isolated workspace → scoped tools/runtime → capture edits + verification → seal immutable proposal against an expected base → review → compare-and-swap apply → revoke and destroy → redacted audit trail remains.
Matapan is a workspace transaction manager — not an IDE, not an agent, not a generic sandbox, not a Git GUI.
Status
0.1.0-beta.1 — all eight planned sprints are landed: security core,
workspace lifecycle (leases + GC), hardened runtime (gVisor experimental),
MCP tool surface, MVP proposal engine, policy profiles, secrets broker,
egress proxy, audit retention, Charon adapter boundary, packaging, and
adversarial hardening (fuzz + edge matrix). See CHANGELOG.md,
docs/MVP-acceptance.md, and docs/docker-limitations.md.
Quickstart
From zero, on macOS or Linux (requires git, Go 1.25.12+ to build from source,
and a local Docker daemon for workspace_run):
# Install (builds and installs to /usr/local/bin, or --user for ~/.local/bin)
scripts/install.sh
# Verify the environment
matapan doctor
# git ok git version 2.50.1
# config ok ~/.matapan/config.json
# database ok ~/.matapan/matapan.db (schema v7)
# obol key ok ~/.matapan/obol.key (perms 600)
# docker ok daemon reachable
# runtime image ok present, digest matches
# runtime spawn ok hardened container ran, exit 0
# Pre-pull the digest-pinned runtime image
matapan runtime pull
# Create a workspace from a git repo (worktree at an exact commit)
matapan workspace create --repo ~/src/my-app --base main
# {"ID": "019f…", "State": "ready", "Branch": "matapan/019f…", …}
# Start the daemon, then connect an agent (see docs/quickstart-claude.md)
matapand &
The agent then drives workspace_file_edit / workspace_run /
workspace_commit against that workspace ID. When the work is done:
# Seal the workspace into an immutable proposal (via the agent or MCP),
# then review and apply with compare-and-swap:
matapan proposal list
matapan proposal show <id> # diff, digests, evidence, lineage
matapan proposal apply <id> --expected-base <commit>
# {"ProposalID": "019f…", "AppliedHead": "7f84…", "Strategy": "auto",
# "Rollback": "git -C ~/src/my-app reset --hard <commit>"}
# Housekeeping
matapan workspace destroy <id> # verified teardown
matapan gc --dry-run # what idle/expired GC would collect
Other source types: --source snapshot --path /dir (defended copy of a
non-Git directory), --source fresh [--git-init] (empty scaffold). Useful
flags: --profile restricted (policy profile), --lease 24h (GC
expiry), --egress proxy.example.com (human egress grant).
Connecting agents
- Claude Code / local agents (stdio):
matapand --stdiowithMATAPAN_OBOLset — see docs/quickstart-claude.md. - ChatGPT (HTTPS tunnel): authenticated StreamableHTTP at
/mcpvia a user-managed tunnel — see docs/quickstart-chatgpt.md.
Tool surface
- Profiles (
mcp.tool_profilein config.json, defaultminimal): minimal is the core 12 tools;fulladds typedworkspace_file_globandworkspace_file_grepfor hosts that prefer typed search over shellrg/find(DevSpace parity). - Capability discovery:
matapan_capabilitiesreturns the server version, profile, tools + annotations, hard clamps, and feature flags so any agent can self-configure. - Typed errors: tool failures return a structured
{"error": {"code", "message"}}field with stable codes (unauthorized,not_found,path_escape,workspace_locked,stale_base,spec_refused,state_conflict,invalid_argument,egress_denied,unsupported,unavailable,internal). - Instructions: root
AGENTS.md/CLAUDE.mdare discovered and returned onworkspace_status, and their digests are recorded in every proposal seal. Instruction content is data for the agent, never control input — it cannot change policy, scopes, or limits. - Egress grants: network stays
noneunless the workspace has a grant from a human (matapan workspace create --egress DOMAINor theworkspace_grant_egresstool, scopeworkspace.grant— agent principals don't get it). Granted runs go through the matapan egress proxy (allowlisting CONNECT/HTTP proxy): HTTP(S) to granted domains works, everything else is denied and ledgered. Enforcement honesty: on native Linux Docker the container attaches to an internal network with no direct route out (full enforcement); on Docker Desktop the container runs on the default bridge with the proxy as its only configured path — HTTP(S) is filtered, but raw-IP egress is a documented gap until the sidecar proxy lands.
Policy profiles, secrets, and identity
- Policy profiles (
default,restricted,open;matapan policy list/show): image allowlists, egress policy, argv[0] command denylists, resource ceilings. Assigned per workspace at create (--profile); the profile digest is sealed into every proposal aspolicy_digest. - Secrets:
matapan secret setstores values AES-256-GCM-encrypted (key at 0600, never plaintext at rest; value read from stdin or--env, never argv). Humans grant secrets to workspaces (workspace_grant_secret/matapan secret grant); runs inject only granted secrets asMATAPAN_SECRET_<NAME>env vars; grants are revoked automatically at proposal seal and workspace destroy. Values are registered with the ledger redactor at injection time. - Charon mode:
auth.mode: charon+auth.charon_urlswitches token validation to the Charon validate endpoint (5s, fail-closed, audiencematapanenforced either way).review.prevent_self_approval(default true in charon mode) blocks a proposal's creator from applying or rejecting it — independent review. - OAuth mode:
auth.mode: oauthmakes the instance a full OAuth 2.0 authorization server for connectors that require OAuth (ChatGPT, Claude). HS256 JWT access tokens (24h, same HMAC key file as obols), public PKCE clientchatgpt-mcp, metadata discovery under/.well-known/, and an owner approval gate: the authorize endpoint renders a form demanding the owner password (MATAPAN_OAUTH_OWNER_PASSWORDat daemon start — hashed in memory, never stored; startup refuses oauth mode without it). No open enrollment: only the configured client and allowlisted redirect URIs are accepted. Tokens map to a connector-specificagent-<client_id>principal (AgentScopes: noworkspace.grant, noproposal.apply) — the connector cannot grant egress or secrets and cannot apply proposals; apply stays human. The approval page renders the exact effective scopes. Obols keep working alongside JWTs.matapan config oauth-setupprints the exact connector instructions. - Audit retention:
ledger.retention_days(90) andledger.max_entries(1e6); the startup/daily sweeper compacts with an anchor entry soVerifystill validates the retained tail.
Runtime configuration
The runtime section of config.json sets per-run defaults
(timeout_sec, memory_mb, pids_limit, nano_cpus,
max_output_bytes). Per-call values may lower these but never raise them
past the hard clamps, which are constants in internal/runtime (not
config): timeout ≤ 10 min, memory ≤ 4 GiB, captured/streamed output ≤ 4
MiB. Images are digest-pinned (docker_image) and verified after every
pull; provenance lives in the runtime_images table
(matapan runtime images).
Configuration precedence and env overrides
Precedence, always: env > file > defaults. Every MATAPAN_* variable
is applied after the config file loads, before validation; invalid values
fail closed with an error naming the variable.
| Variable | Meaning | Default |
|---|---|---|
MATAPAN_LISTEN_ADDR | HTTP bind address | 127.0.0.1:18777 |
MATAPAN_DB_PATH | SQLite control db | ~/.matapan/matapan.db |
MATAPAN_WORKSPACE_ROOT | workspace root dir | ~/.matapan/workspaces |
MATAPAN_DOCKER_IMAGE | digest-pinned run image | alpine:3.23 pinned |
MATAPAN_OBOL_KEY_PATH | obol HMAC key (0600) | ~/.matapan/obol.key |
MATAPAN_SECRET_KEY_PATH | secrets AES key (0600) | ~/.matapan/secret.key |
MATAPAN_ALLOW_LAN | true/false non-loopback bind | false |
MATAPAN_TOOL_PROFILE | minimal/full | minimal |
MATAPAN_AUTH_MODE | local/charon | local |
MATAPAN_CHARON_URL | Charon validate endpoint | — |
MATAPAN_GC_IDLE_HOURS | idle workspace expiry (int) | 72 |
MATAPAN_LEDGER_RETENTION_DAYS | audit retention (int) | 90 |
MATAPAN_LEDGER_MAX_ENTRIES | audit cap (int) | 1000000 |
MATAPAN_RUNTIME_ISOLATION | docker/gvisor | docker |
MATAPAN_OAUTH_CLIENT_ID | OAuth public client_id (oauth mode) | chatgpt-mcp |
Container
scripts/docker-build.sh matapan:local # builds + version-stamps the image
docker run -d --name matapand \
-p 127.0.0.1:18777:18777 \
-v /etc/matapan/config.json:/etc/matapan/config.json:ro \
-v ~/.matapan:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME/src:$HOME/src" \
matapan:local
Path parity is required: the workspace root and any source repos must
be mounted at the same absolute path inside and outside the container —
matapand bind-mounts workspace dirs into run containers and runs git
worktree ops against host paths. See docs/operations.md for the full
container deployment guide (including the per-model compose layout) and
the docker.sock trust note. The image runs as root (documented in the
Dockerfile): the Docker socket's group varies by host; agent workloads
still run under the full hardened profile.
Security summary
- No shell anywhere. All subprocesses are
os/execargv arrays; there is nosh -cpath in the codebase. - Workspace path defense. Every file tool call re-canonicalizes with
filepath.EvalSymlinksand rejects..escapes and symlink escapes from the workspace root, with a TOCTOU re-check on open. - Hardened container runtime. Non-root user,
CapDrop: ALL, no-new-privileges, default seccomp, memory/CPU/PID limits, read-only rootfs,NetworkMode: noneby default (explicit egress grant required), digest-pinned images with post-pull digest verification, explicit env allowlist (no ambient host env), and a hard refusal of privileged mode, host PID/IPC, and any/var/run/docker.sockmount. Containers are namedmatapan-<workspace>-<seq>and labeled so destroy and reconciliation can find them; cleanup is verified, not trusted. Command evidence includes exit code, OOM-kill flag, verified image digest, termination reason, and bounded (truncation-marked) output. - Immutable proposals + CAS apply. A proposal seals the diff, base/head
commits, and a content digest. Apply verifies the target branch head equals
both the proposal base and your
expected_base, merges in a temporary worktree first, and is idempotent on replay. Stale base returns a typed error carrying the current head. - Audit ledger. Append-only, hash-chained, redacted before persistence —
secret values are never stored (
ledger.Redact). - Obol auth. HMAC-SHA256 bearer tokens (
obol_<id>_<secret_hex>), revocable, audience hardcoded tomatapan. - Snapshot import defense. Non-Git sources are copied, never referenced: source root canonicalized, escaping symlinks skipped and recorded, FIFOs/devices rejected, byte/file caps, TOCTOU-defended source opens.
- Lifecycle safety. Per-workspace locking (in-process mutex + flock, crash-safe), destroy allowed from any state with post-delete verification, startup reconciliation for stuck/orphaned workspaces, and Matapan-labeled containers killed on destroy.
Honest isolation language: this is hardened container isolation, not "safe
execution". gVisor/Kata/microVM profiles are the higher-assurance path
(gVisor is experimental as of Sprint 8). Full honest detail:
docs/docker-limitations.md.
Layout
cmd/matapand daemon (MCP over stdio or authenticated HTTP)
cmd/matapan CLI (workspace/proposal/policy/secret/gc/runtime/doctor/config)
internal/config daemon config + obol signing key load/generate
internal/obol HMAC-SHA256 token service (audience "matapan")
internal/auth identity adapter (local obols / Charon validate endpoint)
internal/policy principals, scopes, workspace grants, named policy profiles
internal/ledger hash-chained append-only audit + retention sweeps
internal/idempotency key-based dedup
internal/store SQLite schema, migrations, typed store
internal/workspace lifecycle, worktrees, snapshots, locking, leases + GC,
canonical path defense
internal/reconcile startup crash recovery + orphan reconciliation
internal/runtime hardened Docker exec, verified images, egress proxy wiring
internal/egressproxy allowlisting CONNECT/HTTP egress proxy
internal/secrets AES-256-GCM secret registry + grant lifecycle
internal/proposal seal, CAS apply strategies, conflict handling, revise
internal/testparse JUnit/TAP evidence parsing (matapan-observed)
internal/mcpserver workspace-scoped MCP tools (mcp-go v0.56.0)
internal/httpserver daemon HTTP (/api/health, /mcp)
scripts/ install.sh + release.sh packaging
docs/ threat model, ADRs, quickstarts, operations, acceptance
License
MIT — Copyright (c) 2026 OpenLethe. See LICENSE.