Browse the documentation

Docs Concepts

Guard

Guard sits between your agents and the tools they call, such as a payment service. It checks each call against your rules before the call is sent, and holds a call that needs a person until someone approves or denies it.

On this page

What Guard is#

Guard is a separate program, geo-guard, that runs on your computer. Closing the Geometer app does not stop it. Your agents send their tool calls to Guard instead of straight to the tool, and Guard passes each call on to the real tool. Before it does, it checks the call against the rules you have reviewed. Guard is built to be tried in development and staging. It has not been qualified for production traffic.

Watching first, then stopping#

Guard has two modes, and it starts in the first.

  • Watching. When you set Guard up, it only watches. Every call to the tools it knows still goes through, and Guard writes down what it would have stopped or held. This shows you the effect of your rules on the traffic from your test systems before anything is refused.
  • Stopping calls. A separate decision that you confirm yourself, after you have seen what Guard would have stopped. Guard then refuses calls that break your rules before they are sent, and holds large ones until a person approves them. If Guard cannot decide, it refuses the call unless you chose otherwise.

Geometer never switches Guard to stopping on its own. In the workspace, the Guard screen offers Set up stopping… and asks Stop rule-breaking calls in production? before you confirm with Yes, set it up to stop calls. From a terminal this is geo guard promote --confirm-production. Either way you restart Guard to apply it.

What Guard sees#

Guard only sees calls that go through it. A call that reaches a tool another way, for example with the tool's own password, is not seen and is not stopped. New setups also refuse calls to tools that are not in your rules' list, even while watching, because Guard cannot tell what they do.

Each tool's policy states what Guard stops before the call is sent, what needs the tool's help, and what cannot be checked before a call. The Guard screen shows these under What Guard stops, and what it cannot, and the setup page lists rules Guard cannot cover under Guard cannot stop these before they happen.

Held calls and approvals#

When a rule says a call needs a person, Guard holds it and the call appears on the Guard screen under Waiting for you. For each held call you see what exactly was asked, which rule held it and how fresh your rules are, when the request runs out and who it is waiting for. You can then:

  • Approve this call. The exact call goes through, once. Any change to it needs a new approval.
  • Approve up to an amount… The call goes through, and calls like it up to the amount are allowed for 15 minutes, 1 hour, 4 hours or 1 day. It covers only the rule that held the call.
  • Deny… The call is refused. You can type a reason. The reason is recorded with the decision. Guard's approval request has no place for a reason, so the agent does not receive it, and a retrying agent is only held again.

Editing a call's arguments is not offered, because Guard cannot send edited arguments.

Next to each held call, Guard shows what it knows from its own record: the agent, the rule and policy version, and earlier calls for the same request (the same intent key, meaning the fields that identify it, such as an order) and how they ended. It also says what it cannot see: anything outside Guard, whether the provider already acted unless its reply said so, and anything about the customer beyond the arguments. A section called How approvals have gone counts approvals, denials and expiries over the last 30 days, and how many approvals came within ten seconds of the request, the visible sign of approving without reading. It shows counts only.

Who can approve#

Only people you name can approve held calls, each for the tools and amounts their role covers. Guard's own admin access never approves. When you confirm stopping calls, you become the approver for that tool. You add others on the Guard screen under Who can approve, with Add approver, and you can set the highest amount each may approve. A request goes first to the role with the lowest limit that covers it. If nobody there answers in time, it is passed on to the next role. A person removed later, or whose limit is lowered, cannot have their earlier approval used. A new person's credential is shown once, so you can give it to them. It is not kept on your computer.

Exceptions#

An approver can let specific calls skip one rule for a while, for example one order's refund up to an amount, for at most seven days. Each exception names who granted it and why, and you can end it at any time. An exception never skips the refund-once check, spending limits, a stop or a changed tool.

Stopping calls#

Two controls stop calls quickly. Stop all new calls… makes Guard refuse every new call until you press Allow calls again. You can also stop the new calls of one agent without stopping the others. Anything already sent is not undone. It is listed as still in progress until the tool answers. The terminal commands are geo guard kill and geo guard agent-stop.

When a tool says OK#

A tool answering OK means it accepted the request, not that the money has moved. Guard keeps asking the tool for its own record until it shows the refund finished or failed, and it shows the calls that are not confirmed yet. If an answer is lost, Guard does not send the call again by itself. It asks the tool whether the first call happened. It sends again only when the tool promises to ignore a repeat, and only while that promise still holds. geo guard reconcile settles uncertain calls from the provider's own records and sends nothing.

For a call whose result is not known, Geometer shows advice. It suggests finding out first by asking the provider, and lists other ways to go on: waiting for the provider's confirmation, checking its records by hand, or asking a person. The advice allows nothing. Whatever you or your agent do next is checked again by Guard against your rules, the current approvals and the exact call. If the call, its approval or your rules change, the advice says it is out of date.

Tools that change#

Guard remembers what each tool said it does when it first saw it. If a tool later changes its description or what it accepts, calls to it wait until you look at the change, which the Guard screen lists under Tools that changed. You then choose to accept the change, so calls go through again, or to keep refusing the tool's calls. The first version Guard saw was not checked by anyone; it is only the starting point. In a terminal, geo guard tools lists the pinned tools and geo guard tool-review accepts or rejects a change.

Run Guard in a container#

You can run Guard as a container instead of a program on your computer. This is for development and staging, like the rest of Guard: it does not change that Guard has not been qualified for production traffic.

The image holds only the geo-guard program. It runs as an ordinary user, not as root, and has no shell. Your settings, your secrets and your data stay outside it:

  • Settings. Put guard.toml and your policy bundle in a folder and mount it read-only at /etc/geo-guard.
  • Secrets. Keep the tool's key out of guard.toml. Write Authorization = "${env:PROVIDER_API_KEY}" there, and pass the key when you start the container with -e PROVIDER_API_KEY. Or point to a file, such as ${file:/run/secrets/provider_key}. Guard still reads a key written straight into the file, so older setups keep working, but it warns you each time it starts and on its status.
  • Data. Mount a volume at /var/lib/geo-guard. Guard keeps its store and its record there. If the container is replaced or killed, the next one picks up where it left off.
Terminal
docker run -d --name guard --read-only \
  -v "$PWD/guard:/etc/geo-guard:ro" -v guard-state:/var/lib/geo-guard \
  -e PROVIDER_API_KEY -p 8080:8080 \
  geo-guard:<version>

Point your agents at port 8080. Guard's operator port (approvals, the kill switch, status) is separate; publish it only to places you trust.

Guard in front of a stdio MCP server#

Some agents start their tool server as a program and talk to it over standard input and output. Guard can sit in front of such a server on one computer. This is for development only.

Start the bridge in place of the server: geo-guard mcp-stdio --config guard.toml -- <the server's command>, with the agent's Guard credential in GEO_GUARD_AGENT_TOKEN. Each message the agent writes is decided by the same rules, approvals and tool checks as a call over HTTP, and only an allowed call reaches the server. A refused or held call comes back to the agent as a tool error. The server's own secrets go in a [child_env] table of guard.toml (use ${env:NAME} or ${file:/path}); only the server sees them, never the agent that started the bridge. Messages are handled one at a time in the order they arrive, so a slow call holds up the ones behind it.

Is it healthy? Guard answers /healthz (is it running) and /readyz (should it be sent calls) on a third port, 8081, with no password. They say only "ok", "starting" or "draining". The image checks itself with geo-guard health, so you do not need curl inside it. docker ps shows the result.

Stopping. docker stop tells Guard to finish. It reports "draining" for a second, stops taking new calls, lets the calls already running reach the tool and be recorded, and then exits. If a tool can take a long time to answer, start the container with --stop-timeout set longer than that, so Docker waits for it. Use --restart unless-stopped to bring it back after a crash.

You can also change a few settings with environment variables: GEO_GUARD_LISTEN, GEO_GUARD_ADMIN_LISTEN, GEO_GUARD_HEALTH_LISTEN, GEO_GUARD_UPSTREAM and GEO_GUARD_STATE. The mode (watching or stopping) is only ever set in the file.

Evidence for MCP calls#

Every MCP tools/call that Guard decides, over HTTP or through the stdio bridge (including a server wrapped with geo mcp wrap and set to guard mode), adds one entry to the evidence, next to the decision it belongs to. It holds the server's name ([mcp] server, or the name of the wrapped server, or the tool name of the deployment), the tool, the outcome of the decision (allowed, held, refused, or in watching mode would-hold and would-refuse), a digest of the request, a digest of the reply (or none, lost, or error), and the time. The chain of records is checked as before, by the open verifier.

The entry holds digests, not values. To keep the arguments and the reply as well, set record_values = true under [mcp] in guard.toml. They are then stored as a locked payload apart from the chain, which you can delete or redact with geo prove retention without breaking the chain. The decision's own arguments are, as always, stored with sensitive-looking values already removed.

Your decisions are recorded#

Each step is kept as a separate decision with who made it and when: a rule being proposed, someone accepting what it means, turning it on as a check, and stopping calls in production. Guard's decisions also go into the Prove record. See Prove and certificates. The names you type are stored as typed. This computer has no accounts, so Geometer cannot check who you are.

Next#

Something unclear, wrong or missing on this page? Report a problem with these docs.