Back to directory
duriantaco avatar
duriantaco / gatemole

gatemole

Gatemole compiles human-owned intent into obligations, links evidence artifacts to those obligations, and produces deterministic release decisions for agent-written changes.

17

Stars

2

Forks

1

Watchers

Apache-2.0

License

Gatemole logo

Gatemole

Formerly called Vouch.

Gatemole is an enforcement kernel and transaction Runtime for autonomous agents. Agents may propose work, but Gatemole controls the boundary between that proposal and an exact effect.

Gatemole does not replace an agent framework, model provider, identity provider, container runtime, Kubernetes or the host operating system. It controls the authority and transaction boundary around agent work.

One kernel serves two product experiences:

  • Gatemole Developer Runtime is the local experience for developers who want to run an existing coding agent in an isolated Git transaction, inspect its exact effects and control release. A low-level, manually operated local-Git integration exists today; the self-serve developer preview is still a milestone.
  • Gatemole Agent OS is the target enterprise experience: the same gatemoled kernel plus non-bypassable action connectors, cross-run policy and a Control Plane for a fleet of Runtimes. Those fleet and remote-system capabilities are planned, not implemented.

These are not separate engines. Developer adoption exercises the same kernel semantics that future enterprise deployments require.

What would a team use Gatemole for?

Use Gatemole when a software agent needs permission to change a repository, but should not also receive permission to approve or publish its own work. Gatemole is the control layer around an existing agent; it is not the agent that diagnoses or writes the fix.

Consider a real payments incident, PAY-1842: valid customers are being logged out because the API rejects their refresh tokens. The on-call engineer wants a coding agent to inspect the service, fix the authentication code, add a regression test and use a model while it works.

The pain: the worker is also inside the control boundary

A common direct-agent setup looks roughly like this (the command varies by agent):

cd /srv/repos/payments-api
export MODEL_PROVIDER_API_KEY="$REAL_PROVIDER_KEY"
export GIT_RELEASE_TOKEN="$PAYMENTS_GIT_TOKEN"
payments-coder --task-file tickets/PAY-1842.md

The agent may produce a perfectly good fix. The operational problem is that the team has also placed the live checkout, inherited credentials, network and the account's Git authority inside the same process boundary as the worker. Its transcript is useful context, but it is not an independent authority log. If the process edits an unrelated file, runs the wrong test, crashes halfway or pushes a branch, the surrounding workflow has to discover and recover from that after the fact.

Some agent products already provide their own sandbox, permission prompts or review UI. Gatemole does not claim those features are absent everywhere. Its specific offer is an agent-independent enforcement boundary owned by a separate local daemon:

Control Direct agent invocation Same agent through Gatemole
Task Prompt and runner state Content-bound, durable admission tied to one run and transaction
Workspace Live checkout or the agent product's sandbox Daemon-created private Git worktree; source checkout is not mounted
Model access Provider credential and network policy of the agent process Short-lived broker token; provider credential remains in gatemoled; no model network unless admitted
What changed Agent transcript and a Git diff inspected afterward Daemon-authored normalized effects plus hash-chained execution and transaction receipts
Testing Often requested and run by the same worker Separately pinned verifier runs against the exact frozen tree
Approval External convention or product-specific review Signed decision bound to the immutable approval package
Release Whatever Git authority the process or account has Distinct release identity and compare-and-swap to an allowed local ref
Crash/retry Runner-specific; partial state may need manual interpretation Same admission resumes the transaction; earlier execution receipts remain immutable

payments-coder in both columns is the team's real agent or adapter—not a Gatemole-provided fake agent. The point of the comparison is to keep the worker constant and move authority out of it.

With Gatemole, the team instead uses this workflow:

  1. The operator admits the exact ticket to a named, digest-pinned agent. gatemoled gives the agent a private Git worktree and, if authorized, a transaction-scoped model-broker token. It does not give the agent the source checkout, provider credential, daemon socket or release credential.
  2. Gatemole freezes and inventories the resulting Git effects. The operator can see that the agent changed, for example, the refresh-token implementation and its test—not merely read the agent's description of what it changed.
  3. A separately configured verifier runs the authentication tests against the exact frozen tree.
  4. A security reviewer signs the immutable approval package. The coding agent cannot approve itself, and a later change invalidates that approval.
  5. A different release identity publishes the approved commit to a pre-existing allowed local Git ref with compare-and-swap protection.

After the Runtime has been configured with the payments-coder agent, model broker, verifier, OIDC identities, approval trust and allowed release ref, the day-to-day operator flow is:

export GATEMOLE_IDENTITY_TOKEN="$PAYMENTS_OPERATOR_TOKEN"

gatemole --repo /srv/repos/payments-api run \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments \
  --id tx:pay-1842 \
  --run run:pay-1842 \
  --require-enforcement-profile production \
  --intent-file tickets/PAY-1842.md \
  --agent payments-coder \
  --model-provider openai

gatemole --repo /srv/repos/payments-api review tx:pay-1842 \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments

The operator then asks an independently pinned verifier to test the frozen candidate and prepares the exact local ref update:

VERIFIER_IMAGE="$(cat /etc/gatemole/images/go-verifier.ref)"

gatemole --repo /srv/repos/payments-api tx verify \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments \
  --id tx:pay-1842 \
  --name auth-tests \
  --image "$VERIFIER_IMAGE" \
  -- /usr/local/bin/run-auth-tests

gatemole --repo /srv/repos/payments-api tx prepare \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments \
  --id tx:pay-1842 \
  --git-ref refs/heads/agent-release/pay-1842

The reviewer and releaser use their own short-lived identities:

export GATEMOLE_IDENTITY_TOKEN="$PAYMENTS_REVIEWER_TOKEN"

gatemole --repo /srv/repos/payments-api approve tx:pay-1842 \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments \
  --key /secure/payments-reviewer.key \
  --key-id key:payments-reviewer \
  --approver human:alice \
  --issuer https://login.acme.example/ \
  --class security-reviewer

export GATEMOLE_IDENTITY_TOKEN="$PAYMENTS_RELEASER_TOKEN"

gatemole --repo /srv/repos/payments-api apply tx:pay-1842 \
  --socket /run/gatemole/gatemoled.sock \
  --namespace payments \
  --actor operator:payments-release \
  --actor-kind operator

If the agent or daemon fails, repeating the identical gatemole run resumes the same admitted transaction and retains the earlier execution receipts. It does not silently turn a failed attempt into releasable work.

This is the useful boundary implemented today: governed agent work in a local Git repository. Release updates only the configured local ref; Gatemole does not yet push or merge a pull request, deploy the service, mutate a database or control Kubernetes. Those remote effects require the planned connector and Control Plane layers. The complete setup assumptions and operator walkthrough are in Runtime examples.

Architecture: where the OS, Runtime and kernel sit

Gatemole Agent OS names the complete target system; it is not another process or deployment. The enforcement function lives in one or more customer-side Gatemole Runtime installations. Each Runtime contains a trusted gatemoled kernel. The planned Gatemole Control Plane manages a fleet of Runtimes.

Gatemole Agent OS (the complete product)
|
|-- Gatemole Control Plane [planned]
|   fleet policy | approvals | audit | incident response
|                         |
|                 signed authority
|                         v
|
`-- Gatemole Runtime [customer-side enforcement boundary]
    |
    |<-- agent task
    |
    |-- gatemoled kernel
    |   admission | live authority | budgets | Git journal
    |   effect inventory | sequence policy | approval
    |   local-ref commit                              [current]
    |   action broker | cross-run policy | reconciliation
    |                                                  [planned]
    |
    |-- agent sandbox + adapter --> private Git worktree
    |                                                  [current]
    |
    `-- connector drivers                              [planned]
                  |
                  v
        GitHub | Kubernetes | PostgreSQL | SAP | cloud

Administrators and approvers operate the Control Plane; Runtime receipts flow
back to it for audit and incident response.

The operating-system analogy is precise:

Name Meaning Status
Gatemole Developer Runtime The local product experience around one Runtime: package an agent, execute it in an isolated Git transaction, inspect exact effects and control release. Low-level integration, Runtime profile initialization, diagnostics and exact review/apply/reject shell implemented; packaged adapters, watch and live cancellation planned
Gatemole Agent OS The enterprise product experience and complete target architecture: Control Plane, Runtime fleet, transaction protocol and connector model. It is an umbrella, not a process. Product direction
Gatemole Control Plane Organization-wide fleet, policy, approval, audit and incident management. It manages Runtimes but does not execute agent actions. Planned
Gatemole Runtime The deployable enforcement boundary installed in a customer environment. It contains gatemoled, agent sandboxes, local durable state and connector drivers. Runtime identity, a narrow single-node local-Git profile and exact profile-bound admission are implemented
gatemoled kernel The trusted daemon that owns admission, authoritative lifecycle state, budgets, policy decisions, the transaction journal, approvals and commit coordination. Runtime preflight, atomic task admission, live OCI authority revalidation, paired execution settlement/recovery and supported budget charging implemented; supervision and action enforcement are still converging
Agent sandbox The isolated, untrusted environment in which an agent loop executes. It receives no downstream production credentials. OCI implementation available
Agent adapter Connects an existing agent framework or command to the kernel. It may request work and actions but cannot authorize itself or create receipts. Command/profile and lower-level integration exist; supported broker API planned
Connector driver Performs typed operations against one downstream system after kernel authorization and reconciles external state. gatemoled records authoritative receipts and coordinates recovery. Generic interface and remote drivers planned. Local Git currently uses a dedicated transaction path, not that future interface
Gatemole Contracts Optional verification module that turns human-owned intent into evidence obligations used by Runtime policy. Beta

The diagram states the intended ownership boundary, not a completion claim. Today gatemole run first asks the selected daemon to match the repository's Runtime identity, report the requested enforcement profile and inspect the selected digest-pinned image. This preflight happens before task authority or a worktree is created. Task admission then durably binds the exact Runtime ID and daemon enforcement profile with the content-bound contract, real run, initial grants and transaction. Before OCI launch, gatemoled reloads that live authority and fail-closed derives the permitted image, command, full-workspace access, model-broker access and deadline. It then atomically verifies the admitted run and transaction heads while recording execution start in both ledgers, before starting any broker or agent workload. Settlement atomically closes the same execution in both ledgers, clears the run's active-execution binding and durably charges elapsed wall time plus verified model-call and token usage against the remaining hard limits. The raw transaction receipt retains the execution timestamps and counters if a charge saturates. Daemon restart performs the same paired settlement for an interrupted workload and marks unfinished model calls unknown before charging them. Live cancellation, tool/cost accounting and the mediated connector action path remain later Runtime milestones.

The target remote-effect path is:

agent proposes an action
  -> gatemoled authenticates the run and delegated authority
  -> gatemoled evaluates the action and complete transaction sequence
  -> deny | request approval | authorize
  -> connector driver stages or executes with scoped credentials
  -> gatemoled records the receipt and verifies the outcome
  -> commit | compensate | reconcile | require manual recovery

This broker-and-connector path is not implemented in the current production profile; the older action endpoints are disabled there. Today the contained effect boundary is mutation of a private Git worktree followed by exact inspection, verification, approval and a local-ref compare-and-swap.

An agent with direct downstream credentials can bypass Gatemole. A future multi-system deployment is therefore enforcement-grade only when production credentials and network paths are available exclusively through Gatemole connector drivers.

The current local-Git transaction is the first kernel transaction-and-effect slice. A complete Gatemole Agent OS external claim requires both non-bypassable, multi-system Runtime enforcement and a Gatemole Control Plane managing a fleet of those Runtimes.

Developer Runtime: current local-Git integration

The current runtime requires Git, an OCI engine such as Docker, a running gatemoled, and a digest-pinned agent image.

This is a low-level developer integration, not yet a self-serve desktop agent environment. gatemole runtime init creates a strict repository-owned profile and a local Runtime identity. gatemole doctor diagnoses Git, OCI, profile, local-image and daemon readiness. gatemole review shows the daemon-bound status, exact effects, evidence IDs and frozen patch; diff, apply and reject expose the corresponding task-oriented operations. Packaged adapters, daemon supervision, watch and live cancellation remain roadmap work.

Build the CLI from source:

go install ./cmd/gatemole
gatemole version
gatemole --help

In a Git repository with a HEAD commit, register an agent image that is already present in the local OCI engine:

gatemole --repo /path/to/service runtime init \
  --agent coding-agent \
  --image registry.example/coding-agent@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
  --source-digest sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
  -- /usr/local/bin/agent

--source-digest identifies the source or build input used for that image; it is deliberately not invented by Gatemole. Initialization also creates .gatemole/runtime.json, a random local Runtime instance identity, and rules that keep it, SQLite/WAL files, locks and the daemon socket out of Git. Commit .gatemole/agent-profiles.json and .gatemole/.gitignore; do not commit .gatemole/runtime.json.

This is a hard pre-1.0 state cutover. If a repository still contains .vouch, Gatemole fails before writing any new state; it never merges, renames, or rehashes old configuration, ledgers, evidence, or signatures automatically. Archive or remove the legacy directory deliberately, then initialize .gatemole.

Start the repository-local development daemon in one terminal:

gatemole --repo /path/to/service daemon

By default, transaction worktrees live in a canonical, repository-scoped directory beneath the current user's validated runtime or cache directory, not in a shared predictable /tmp/gatemole-transactions path. A custom --transaction-root is validated before the ledger opens.

In another terminal, diagnose the selected integration and run it:

gatemole --repo /path/to/service doctor --agent coding-agent

gatemole --repo /path/to/service run \
  --intent "Fix authentication without changing public behavior" \
  --agent coding-agent

Doctor warnings, such as an intentionally stopped daemon or omitting --agent, do not make the command fail. A selected missing image, invalid profile, unavailable OCI engine or unready existing daemon does. When the socket exists, doctor uses the daemon's authoritative Runtime preflight instead of treating a separate CLI-side OCI probe as proof.

For a developer, the integration contract is deliberately small:

  1. Package the existing agent as a digest-pinned OCI image.
  2. Make its command read the retained task at $GATEMOLE_TASK_PATH.
  3. Let it edit only /workspace and return a normal process exit code.
  4. Start it with gatemole run; do not give the container the daemon socket, repository credentials or downstream production credentials.
  5. Inspect the transaction and its hash-chained events before verification, approval and release.

For example, an agent entrypoint can begin with:

#!/bin/sh
set -eu
test "$GATEMOLE_RUNTIME_ROLE" = agent
test -r "$GATEMOLE_TASK_PATH"
cd /workspace

# Invoke your existing agent loop here. All intended file effects stay in this
# detached transaction worktree.
exec /opt/my-agent --task-file "$GATEMOLE_TASK_PATH"

gatemole runtime init writes the strict, repository-owned .gatemole/agent-profiles.json and local-only .gatemole/runtime.json. Select the agent profile by name:

gatemole --repo /path/to/service run \
  --intent "Fix authentication without changing public behavior" \
  --agent coding-agent

The public profile schema is schemas/gatemole.agent_profiles.v0.schema.json, with a complete example profile. Gatemole binds the selected profile, final command, pinned image and exact task intent into a durable task envelope. For OCI execution, gatemole run first preflights the exact local Runtime ID, enforcement profile and image with gatemoled; a failure creates neither task authority nor a worktree. Admission v1 then binds that Runtime ID and the daemon's actual enforcement profile into the durable transaction. Daemon-owned OCI agents receive the task envelope read-only at /gatemole/task.json.

The product CLI loads .gatemole/runtime.json and binds every daemon call from the run, transaction, low-level kernel and action surfaces to that ID. Preflight and current task admission carry the expected ID in their validated bodies; lifecycle mutations, reads and long-running agent/verifier operations also carry Gatemole-Runtime-ID. A configured daemon rejects a missing or different header before those handlers run. This is exact Runtime correlation, not a secret or an authentication credential.

Model egress is absent unless the task explicitly requests the configured daemon broker, for example with --model-provider openai. The provider credential never enters the agent container:

# No model authority: network=none and no model credential in the container.
gatemole --repo /path/to/service run \
  --intent "Apply the deterministic migration" \
  --agent migration-agent

# Explicit model authority: only the configured OpenAI-compatible broker is
# reachable. OPENAI_API_KEY is a transaction-scoped broker token, not the
# provider credential.
gatemole --repo /path/to/service run \
  --intent "Fix the failing authentication test" \
  --agent coding-agent \
  --model-provider openai

At launch, the kernel derives the run identity, image, command, workspace access, broker access and deadline from live admitted state. A stale run, an expired grant, a changed image or command, a narrower unsupported workspace grant, or an ungranted model provider fails before any broker or agent container starts.

For the complete PAY-1842 operator workflow and the assumptions behind it, see Runtime examples. That guide separately labels its runnable deterministic fixtures as acceptance proofs rather than presenting them as the normal user experience.

gatemole run then creates the isolated worktree, runs the agent, freezes its Git effects, and performs deterministic sequence validation. Inspect the result with:

gatemole --repo /path/to/service review <transaction-id> \
  --namespace local

# Print only the exact frozen patch (safe to pipe to another tool):
gatemole --repo /path/to/service diff <transaction-id> \
  --namespace local

# Advanced lifecycle/debugging surface:
gatemole --repo /path/to/service status <transaction-id> \
  --namespace local

gatemole --repo /path/to/service tx effects \
  --namespace local --id <transaction-id>

gatemole --repo /path/to/service tx events \
  --namespace local --id <transaction-id>

The daemon re-inspects the staged tree before returning a diff. The client checks its patch digest and transaction/attempt/event binding; a worktree change after staging fails closed instead of showing a mutable approximation. After verification, preparation and any required signed approval, gatemole apply <transaction-id> invokes the same release operation as gatemole release. gatemole reject <transaction-id> aborts and discards an unreleased worktree; it is not live workload cancellation.

Optional local proof: crash and resume the PAY-1842 agent

This is an executable acceptance fixture, not the operator interface described above. Run it from a Gatemole source checkout to exercise the failure/recovery invariant without supplying a real agent, repository or production trust configuration. It requires Git, Go, Docker and jq; Docker may fetch golang:1.26-alpine the first time:

scripts/gatemolepairedexecutiondemo.sh

The fixture builds a disposable payments API and deterministic agent. Its first attempt saves the candidate fix in the private transaction worktree and exits with code 42; after a daemon restart, the second attempt resumes that patch and runs go test ./internal/auth successfully.

The final JSON report includes both immutable execution receipts, all four paired run.execution_started/run.execution_finished events, cumulative wall time across both attempts, a waiting_for_event run with no active execution, and proof that the developer's source checkout was never modified. See Runtime examples for the expected output and equivalent inspection commands.

Production verification and preparation use the advanced tx verify and tx prepare operations. Reviewers and releasers use the top-level gatemole review, gatemole approve and gatemole apply commands under the hardened daemon configuration. See Production Runtime Operations for the complete deployment contract; do not infer production safety from the development example above.

Production callers should make the expected profile explicit:

gatemole --repo /srv/gatemole/repository run \
  --socket /run/gatemole/gatemoled.sock \
  --namespace engineering \
  --require-enforcement-profile production \
  --intent "Fix the approved authentication regression" \
  --agent coding-agent

A development daemon cannot satisfy that command, so it fails during preflight before admission or worktree creation.

What the runtime enforces today

The implemented runtime includes:

  • Daemon-owned OCI agent and verifier execution.
  • Detached, task-scoped Git worktrees.
  • Immutable Git-tree staging and exact read-only verifier materialization.
  • A normalized, ordered effect ledger and deterministic sequence policy.
  • Hash-chained transaction events and crash-recoverable SQLite projections.
  • Digest-pinned agent, verifier and model-broker images.
  • Policy-controlled model access without exposing the provider credential to the agent.
  • OIDC operator identity, signed approval packages and separation of approver and releaser.
  • A repository-local Runtime identity, stable Runtime and ledger locks, same-UID Unix peer authentication, daemon-authoritative preflight, and Runtime/profile-bound v1 admission.
  • A private, owner-validated transaction staging root and immutable startup snapshots for trust, verifier and model-broker policy inputs.
  • Atomic compare-and-swap publication to an allowed local Git ref.
  • Bounded Git inspection, workload concurrency, output capture and readiness checks.

Supported deployment profile

The current supported profile is deliberately narrow:

  • One node and one security tenant.
  • A dedicated trusted host or VM and non-root daemon account.
  • One repository and SQLite ledger per runtime boundary.
  • One local Runtime identity bound to that ledger and enforcement profile.
  • Preloaded, digest-pinned OCI images.
  • Required verifier profiles and logically independent signed approval.
  • Publication to an allowed local Git ref only.

It does not push to a remote, create or merge pull requests, deploy software, coordinate database or Kubernetes effects, provide multi-tenant isolation, expose a remote control API, or provide high availability. Stable versioned release packaging is also still pending. The exact host, storage, identity, quota and acceptance requirements are documented in docs/PRODUCTION.md.

The Runtime identity, repository-local .gatemole/runtime.lock, and ledger lock prevent an accidental same-host, same-UID daemon or ledger mix-up even when environment or database paths differ. They are not fleet enrollment, cryptographic same-UID daemon attestation or cross-host attestation. Use a dedicated OS account for a hardened Runtime. Deliberately copying all ignored identity and ledger state to another repository copies the trust target too. Organization enrollment, attested Runtime identity and fleet revocation belong to the planned Control Plane.

Every CLI process that connects to the hardened Unix socket must use that daemon account's effective UID. OIDC tokens still distinguish operators, reviewers and releasers, but the current approval CLI has no separate offline sign-and-submit path; see the production guide before claiming OS-level reviewer-key custody separation.

Gatemole Contracts

Repositories that need semantic release obligations can opt into the Contracts module:

gatemole --repo /path/to/service contracts try --write
gatemole --repo /path/to/service contracts compile
pytest --junitxml .gatemole/artifacts/pytest.xml
gatemole --repo /path/to/service contracts evidence import junit \
  .gatemole/artifacts/pytest.xml
gatemole --repo /path/to/service contracts gate

Contracts turn human-owned intent into stable obligation IDs and determine whether supplied evidence covers those obligations. They do not prove arbitrary code correct and are not a generic AI code reviewer.

Documentation

Published documentation: https://duriantaco.github.io/gatemole/

Validation

The repository maintains separate acceptance paths for the Contracts module, kernel semantics, transactions, runtime execution and the supported production profile:

go test ./...
go vet ./...
go run golang.org/x/vuln/cmd/[email protected] ./...
go test -race -count=1 ./internal/kernel/...
scripts/gatemolebench.sh
scripts/gatemolekernelbench.sh --out /tmp/gatemolekernelbench
scripts/gatemoletransactionbench.sh
scripts/gatemoleruntimebench.sh
image="$(scripts/gatemoleproductionfixture.sh --tag gatemole-production-fixture:acceptance)"
GATEMOLE_PRODUCTION_IMAGE="$image" scripts/gatemoleproductionbench.sh

Passing these gates establishes the documented invariants for that revision. It does not establish correctness for arbitrary agent output or expand the supported deployment boundary.

License

Apache-2.0. See LICENSE.