Help us shape the future of Go! Take the 2026 Go Developer Survey

agent

package
v1.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package agent triages the reach of an agentic system's tool chain.

geiger's other modules answer "what does this credential unlock". That is the wrong unit for an agent: a config whose every secret is stored correctly — OS env, a keychain, an OAuth flow — can still wire the agent to a filesystem server rooted at /, a shell server, and the corporate wiki. The credential hygiene is irrelevant to the blast radius; the TOOL CHAIN is the unit.

The package types each configured server into a set of reach primitives (offline, from the catalog and from argv/env heuristics), optionally confirms them by enumerating the server's real tool list read-only, and reports the chains that are worth more than the sum of their parts — the lethal trifecta, bulk read plus a way out, an exec tool, a secret store.

That primitive list is inventory and does not set a tier on its own: a config file cannot say whether the reach it describes is appropriate for the machine it sits on. Severity comes from the chains. See the note at the top of findings.go.

See docs/design/agentic-reach.md.

Index

Constants

This section is empty.

Variables

View Source
var ErrSpawnNotPermitted = errors.New("stdio enumeration requires --live --spawn-stdio")

ErrSpawnNotPermitted is returned when stdio enumeration was not authorized.

Functions

func EnumerateRemote

func EnumerateRemote(ctx context.Context, c *recon.Client, s *Server)

EnumerateRemote lists a remote server's tools read-only. It tries the stateless 2026 shape first and falls back to the 2025 handshake, so a server on either side of the break is enumerated by the same call.

func EnumerateStdio

func EnumerateStdio(ctx context.Context, s *Server, o SpawnOptions)

EnumerateStdio starts a local stdio server, asks it for its tool list, and tears it down. It is a no-op unless both gates are open.

func ServerMaps

func ServerMaps(root map[string]any) map[string]any

ServerMaps returns the server map from any known config layout. Clients agree on the per-server shape and disagree on where the map lives, so this is the only place the layouts differ.

func ToolNames

func ToolNames(tools []Tool) []string

ToolNames returns just the names, for the note's detail expansion.

func Type

func Type(s *Server)

Type fills in a server's static capabilities from the catalog and the argv/env heuristics. It never runs anything.

Types

type Cap

type Cap uint32

Cap is one reach primitive. A tool, a server, and a whole agent surface are each described by a set of these.

const (
	// CapExec runs code on the host: shell, python eval, docker, k8s exec, a CI
	// trigger. The strongest primitive there is — it closes over every other
	// credential on the box.
	CapExec Cap = 1 << iota
	// CapCorpusSearch searches or bulk-reads a whole document corpus: wiki,
	// ticketing, chat, drive, mail, code search, a vector index. Separated from
	// CapDataRead because it needs no chaining to be an incident: "give me every
	// credential in Confluence" is one tool call, and it is the highest-yield
	// move in a real engagement.
	CapCorpusSearch
	// CapSecretsRead reads a secret store, environment, or keychain — the edge
	// from the tool chain into every credential geiger already triages.
	CapSecretsRead
	// CapCodeWrite pushes code, opens PRs, or publishes packages (supply chain).
	CapCodeWrite
	// CapCloudControl reaches a cloud control plane.
	CapCloudControl
	// CapDestructive deletes, wipes, or terminates.
	CapDestructive
	// CapIdentityAdmin writes to an IdP or directory.
	CapIdentityAdmin
	// CapFSRead / CapFSWrite touch the local filesystem. Their weight depends
	// entirely on the root scope, which is why Capability carries one.
	CapFSRead
	CapFSWrite
	// CapDataRead reads scoped private data: a record, a bounded table. The
	// non-bulk sibling of CapCorpusSearch.
	CapDataRead
	// CapNetEgress sends data outward: fetch with a caller-controlled URL, a
	// webhook, mail, a chat post. The exfiltration half of every data chain.
	CapNetEgress
	// CapUntrustedIn ingests attacker-influenceable content: the web, GitHub
	// issues, mail, tickets. The prompt-injection entry point.
	CapUntrustedIn
)

func (Cap) Flag

func (c Cap) Flag(broadScope bool) module.FlagLevel

Flag says how a capability line is reported.

Inventory carries no weight, so the answer is normally FlagNone. The one exception is filesystem reach at a broad root: a server rooted at / or $HOME and one rooted at ./project are the same package two orders of magnitude apart, and the config says which it is.

func (Cap) Name

func (c Cap) Name() string

Name is the short stable identifier used in output and JSON.

func (Cap) Why

func (c Cap) Why() string

Why is the one-line reason this primitive matters, written for someone deciding what to look at first.

type Capability

type Capability struct {
	Cap Cap
	// Scope bounds the primitive where one is knowable — a filesystem root, a
	// database name, a corpus host. Empty means unbounded or unknown.
	Scope string
	// Broad marks a scope that is effectively unbounded (a filesystem server
	// rooted at / or at $HOME, a wildcard host).
	Broad bool
	// Evidence is what typed it: a catalog package name, an argv pattern, or an
	// enumerated tool name.
	Evidence string
}

Capability is one primitive as found on a specific server, with the scope it applies at and the evidence for it.

func (Capability) String

func (c Capability) String() string

String renders the capability for a finding line.

type Caps

type Caps []Capability

Caps is an ordered, deduplicated list of capabilities.

func ClassifyTools

func ClassifyTools(tools []Tool) (Caps, int)

ClassifyTools types an enumerated tool list. It returns the capability set and the number of tools no rule matched, so the note can state its own coverage rather than implying the list was fully understood.

func (Caps) Add

func (cs Caps) Add(c Capability) Caps

Add appends a capability, merging into an existing entry for the same primitive rather than repeating it. The broader scope wins, since that is the reach an operator has to assume.

func (Caps) BroadFS

func (cs Caps) BroadFS() bool

BroadFS reports whether any filesystem capability is at a broad scope.

func (Caps) Merge

func (cs Caps) Merge(o Caps) Caps

Merge folds another list in.

func (Caps) Set

func (cs Caps) Set() Set

Set collapses the list to a plain primitive union.

func (Caps) Sorted

func (cs Caps) Sorted() Caps

Sorted returns the capabilities in report order (worst first).

func (Caps) Summary

func (cs Caps) Summary() string

Summary renders the capability set as a compact, worst-first phrase for a note title or one-line summary.

type Chain

type Chain struct {
	Name string
	// Why states the composition in the operator's terms.
	Why string
	// Via names the servers that supply each leg, so the reader can act on it.
	Via []string
	// Flag is the finding's significance.
	Flag module.FlagLevel
}

Chain is one composition found on a surface.

func Chains

func Chains(s Surface) []Chain

Chains returns every composition present on the surface, worst first.

type Hook

type Hook struct {
	Event   string
	Command string
}

Hook is one lifecycle shell command.

type Runtime

type Runtime string

Runtime names the agent client a surface was read from.

type Server

type Server struct {
	Name      string
	Transport Transport
	Command   string
	Args      []string
	URL       string
	// EnvNames are the env var names the server config sets, values omitted —
	// the names alone say which credentials the server is handed.
	EnvNames []string
	// HeaderNames are the auth header names on a remote server.
	HeaderNames []string

	// Caps is the union of statically-typed and enumerated reach.
	Caps Caps
	// Label is the catalog's human name for the server, when it matched.
	Label string
	// Unpinned marks a launcher that refetches the package at every start
	// (npx -y, uvx, pipx run without a version). The capability set has no shelf
	// life: the code that runs tomorrow is not the code typed today.
	Unpinned bool
	// PlaintextHTTP marks a remote server reached over http:// — the token in
	// its headers crosses the wire in clear.
	PlaintextHTTP bool
	// AutoApproved lists the tools (or "*") this server is pre-approved for by
	// the runtime's configuration, so no human sees the call.
	AutoApproved []string

	// Enumerated is set once a real tool list was observed. Until then every
	// capability above is geiger's claim about a package name, not a fact.
	Enumerated bool
	// Tools are the enumerated tool names.
	Tools []string
	// ResourceCount / PromptCount are the enumerated resource and prompt counts.
	ResourceCount int
	PromptCount   int
	// Asked records that geiger actually sent the server a request. It separates
	// "we tried and it did not answer" from "we never tried", which are
	// different answers to the reader's question and need different advice.
	Asked bool
	// EnumErr explains why enumeration did not happen or did not succeed.
	EnumErr string
	// Unauthenticated records that the remote server answered tools/list with no
	// credential at all — its whole tool surface is available to anyone who can
	// route to it.
	Unauthenticated bool
	// AuthServer is the authorization server named by a 401's protected-resource
	// metadata (RFC 9728), when the server gates access.
	AuthServer string
}

Server is one configured MCP server, before or after enumeration.

func (Server) CredentialEnvNames

func (s Server) CredentialEnvNames() []string

CredentialEnvNames returns the env var names that hand this server a credential. Used to say, in the note, which of geiger's other findings the server inherits.

func (Server) Invocation

func (s Server) Invocation() string

Invocation is the lowercased string the catalog matches against: the command and arguments for a stdio server, the host and path for a remote one.

func (Server) OpenData

func (s Server) OpenData() bool

OpenData reports the stronger case: the server served CONTENT, not just the names of its tools, to the same credential-free request.

The distinction decides the weight. A hosted MCP endpoint commonly publishes its tool list on purpose and gates the calls, and geiger cannot tell the difference — it never issues tools/call, which is the line between enumerating reach and exercising it. So a public tool list is a warn: it says what the server offers, not that anyone can use it. Resources are the other case. They are the data itself, and they came back to a caller with no credential at all.

func (Server) OpenSurface

func (s Server) OpenSurface() bool

OpenSurface reports an unauthenticated tool surface worth reporting: the server answered geiger's credential-free request, and it is not on this machine.

Loopback stands the finding down for the same reason cleartext to loopback does. A server on 127.0.0.1 is the normal way to run one, "anyone who can route to it" there is a process already on the host, and reporting it would put every developer laptop at the top of the scale.

type Set

type Set uint32

Set is a union of primitives.

func SetOf

func SetOf(cs ...Cap) Set

SetOf builds a set from primitives.

func (Set) Add

func (s Set) Add(c Cap) Set

Add returns the set with c included.

func (Set) Empty

func (s Set) Empty() bool

Empty reports whether no primitive is set.

func (Set) Has

func (s Set) Has(c Cap) bool

Has reports whether c is present.

func (Set) HasAny

func (s Set) HasAny(cs ...Cap) bool

HasAny reports whether any of cs is present.

func (Set) List

func (s Set) List() []Cap

List returns the present primitives in report order.

func (Set) Names

func (s Set) Names() []string

Names returns the present primitives' short names, worst first.

func (Set) Union

func (s Set) Union(o Set) Set

Union merges two sets.

type SpawnOptions

type SpawnOptions struct {
	// Permitted is the --spawn-stdio gate. False means no process is started.
	Permitted bool
	// Live mirrors --live; both must hold.
	Live bool
	// Timeout overrides spawnTimeout (tests).
	Timeout time.Duration
	// Record receives the argv about to be executed, for the audit trail. It is
	// called BEFORE the process starts so a killed or hanging spawn is still
	// attributable.
	Record func(argv string)
}

SpawnOptions controls local stdio enumeration.

type Surface

type Surface struct {
	Runtime Runtime
	File    string
	Servers []Server
	// SkipPermissions records a runtime-wide bypass of the approval prompt
	// (Claude Code's dangerouslySkipPermissions, Cline/Roo YOLO mode). It is the
	// single most consequential setting in an agent config: it removes the human
	// from every chain at once.
	SkipPermissions bool
	// AllowRules are the runtime's blanket pre-approvals (Claude Code's
	// permissions.allow), which auto-approve matching tool calls.
	AllowRules []string
	// Hooks are shell commands the runtime runs on lifecycle events. They
	// execute with NO model in the loop at all, which is strictly more reach
	// than any MCP tool on the same surface.
	Hooks []Hook
	// Skills and Subagents are instruction sets the agent loads and follows.
	Skills    []string
	Subagents []string

	// Live and SpawnStdio record which enumeration the run was allowed to do,
	// so the evidence line names a flag the reader has not already passed.
	Live       bool
	SpawnStdio bool
}

Surface is a parsed agent configuration.

func ParseSurface

func ParseSurface(file string, root map[string]any) Surface

ParseSurface builds a Surface from a decoded agent config. It is layout-aware but transport-agnostic: nothing here runs or dials anything.

func (Surface) AutoApproved

func (s Surface) AutoApproved() bool

AutoApproved reports whether the surface's calls can proceed without a human: a runtime-wide bypass, a blanket allow rule, or per-server pre-approval.

func (Surface) Caps

func (s Surface) Caps() Caps

Caps is the union of every server's reach — what the agent can touch through its tool chain as a whole.

func (Surface) Enumerated

func (s Surface) Enumerated() bool

Enumerated reports whether any server's real tool list was observed. Until one was, every capability on the surface is geiger's reading of a package name, which is what the note's evidence line says.

func (Surface) Findings

func (s Surface) Findings() []module.Finding

Findings renders the surface as note findings, worst first.

func (Surface) Summarize

func (s Surface) Summarize(title string) module.Note

Summarize builds the note and applies the Undetermined rule.

A config is not a credential. For a credential, Undetermined means geiger could not establish the thing is even live, so putting a tier on it would invent a severity. Here the config file is the observation: "server-filesystem /" in the arguments is read off disk, and what it grants is not in doubt. Only the exact tool list is, and that changes precision, not the class of reach.

Withholding a tier until enumeration would also make the common case useless. Most servers are local, and enumerating those needs --spawn-stdio, which runs third-party code and often cannot be run at all — so the default mode, the one people actually use, would never report anything.

Undetermined is kept for the case it was meant for: servers are configured but nothing about their reach could be established.

func (Surface) Summary

func (s Surface) Summary() string

Summary is the one-line takeaway: runtime, size, worst reach.

func (Surface) Untypeable

func (s Surface) Untypeable() bool

Untypeable reports that servers are configured but nothing is known about what any of them exposes.

type Tool

type Tool struct {
	Name        string
	Description string
}

Tool is one enumerated tool.

type Transport

type Transport string

Transport is how the client reaches a server.

const (
	// TransportStdio launches a local process. Enumerating it means EXECUTING
	// the configured command, which is why it is gated behind --spawn-stdio.
	TransportStdio Transport = "stdio"
	// TransportHTTP is a remote server over streamable HTTP (or the deprecated
	// HTTP+SSE). Enumerating it is an ordinary read-only HTTP call.
	TransportHTTP Transport = "http"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL