Understanding

Security model

Grenz answers one question: may this agent do this, right now? This page is about what that buys you, and — just as importantly — what it does not. A security tool that overstates its boundary is worse than one that draws a smaller boundary honestly.

The invariants

These hold everywhere in the codebase, and the tests exist to keep them holding:

  1. Real credentials exist only inside the proxy. They are decrypted in memory and injected into outbound requests. They never appear in a log line, an error message, an approval prompt, or the cloud plane.
  2. The agent holds only a GRENZ_TOKEN. If an upstream credential ever moved toward the agent, that would be a critical bug, not a configuration choice.
  3. Deny by default. An unmatched request, a malformed policy, a vault failure — every one resolves to a denial with a structured reason. Confusion never means “allow”.
  4. The policy engine is pure and local. It performs no IO and makes no network call. A decision cannot hang, fail open on a timeout, or depend on a service being reachable.
  5. The cloud plane distributes policy; it never decides. Losing it means your proxies keep enforcing the last policy they verified — not that they stop enforcing.

What is inside the trust boundary

Grenz runs on your machine, as you. Any process running as your user is inside the boundary. It can read the same files the proxy can, including the vault identity. Grenz constrains what an agent can reach through it; it does not defend your machine against code you have already chosen to run on it.

This is the honest framing of each layer we have built:

LayerWhat it actually bounds
PolicyWhich actions an agent may take, per tool, per target
Budgets & schedulesHow much, and when — the blast radius of a correct-looking agent gone wrong
Kill switchTime-to-stop once you notice, measured in seconds and applied mid-flight
Token expiryHow long a leaked token stays useful. Bounds a leak in time
Socket modeWho can reach the agent listener — your own OS user only. Bounds it in reach. It does not authenticate which process is the agent

Socket mode, precisely

With listen.socket set, agent traffic moves onto a unix domain socket and the admin plane stays on TCP loopback. The claim is narrow and worth stating exactly: the agent listener is reachable only by your own OS user.

What that removes is reach from outside your uid — other users on a shared box or CI runner, browser-origin probes against a loopback port, and containers. That last one is the underrated case: on Docker Desktop any container can reach a host process bound to 127.0.0.1 through host.docker.internal, which binding to loopback does not prevent and a unix socket does.

What it does not remove: your own shell, and anything else running as you. Peer-credential binding — tying the listener to one specific process — is not achievable on the current runtime, and we do not imply otherwise.

The request log is not evidence

Grenz keeps a local SQLite log of decisions so you can see what your agents are doing. It is plain, readable, and truncatable, and it makes no integrity guarantees. Anyone who can write to the file can change it.

This is deliberate, and it is a boundary we hold rather than a gap we intend to close. Grenz is a pre-action permissioning layer. It is not an evidence product, and building tamper-evident logging into it would mean claiming a property the surrounding system cannot support — a log signed by a process running as the same user who can rewrite it proves considerably less than it appears to. If you need that property, it belongs somewhere Grenz is not.

Grenz is not a substitute for upstream scoping

Scope your GitHub token, your Linear key, your database role as tightly as that service allows, and then put Grenz in front. Grenz narrows what an agent may do with a credential; it cannot narrow what the credential itself is capable of if it escapes. Defense in depth means both, not either.

It sees requests, not intentions

Grenz inspects what an agent does — the calls it makes, in order, with what arguments. It has no view into the model's reasoning, and cannot tell a well-intentioned mistake from a deliberate one. Policy is the mechanism precisely because it does not require reading minds: an action either is or is not within what you granted.

Reporting something

If you find a way to reach an upstream credential, bypass a deny, or make the proxy forward something the policy refused, please open an issue on GitHub. If it is sensitive, say so first and we will find a private channel.

Related: the policy format for how decisions are reached, and the quickstart to see the guardrails fire.