Documentation
¶
Overview ¶
Package policy is the fail-closed gate every tool call passes through. A decision comes from three things: the tool's capability class, any per-tool override, and whether the session has ingested untrusted outside content. The default posture is conservative on purpose; this is the difference between an agent that helps and one that runs whatever a fetched web page told it to.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Scrub ¶ added in v0.2.2
func Scrub(raw json.RawMessage) json.RawMessage
Scrub returns a copy of a tool input with credential-shaped values replaced by a marker, so the audit log can record what ran without ever storing a secret it carried. A visibility feature that leaks keys is worse than none; this is the boundary that keeps `tomo watch` and audit.log safe to read.
Input that is not valid JSON is returned unchanged: the auditor writes what the gate saw, and we do not have a structure to reason over. The common case, a tool's JSON arguments, is walked field by field.
Types ¶
type Approver ¶
Approver answers ask decisions. Each channel supplies its own: a y/n prompt on the terminal, inline buttons on Telegram, a click in the web UI.
type Auditor ¶
type Auditor interface {
Record(Entry)
}
Auditor records what happened. A nil Auditor is fine; the guard skips it.
type Config ¶
type Config struct {
Read string `yaml:"read"`
Net string `yaml:"net"`
Write string `yaml:"write"`
Exec string `yaml:"exec"`
Rules map[string]string `yaml:"rules"`
}
Config is the policy section of the config file. Class defaults set the baseline; Rules override by exact tool name and win over the class default.
type Decision ¶
type Decision string
Decision is what the engine says about a call.
func ParseDecision ¶
ParseDecision reads a decision from config, defaulting unknown or empty values to Ask so a typo fails closed rather than open.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine evaluates decisions. The zero value is not useful; build with New.
func New ¶
New builds an engine from config, filling any unset class with the safe default: reads and network calls run, writes and code execution ask.
func (*Engine) Decide ¶
Decide returns the decision for one call. Tainted is true once the session has pulled in untrusted external content. A tainted write or exec that would normally run is escalated to ask because the instructions may no longer be entirely the user's. A deny rule always wins. An allow rule is re-confirmed after taint because approving a trusted writer in clean sessions does not let injected content drive it.
func (*Engine) External ¶ added in v0.2.5
External reports whether name belongs to code outside tomo's reviewed tool surface. The guard uses this after a successful call because an external tool can return injected content regardless of its declared capability class.
func (*Engine) MarkExternal ¶
MarkExternal flags tools that come from outside tomo, such as those served by an MCP server, bridged from a CLI, or reached through ant. They default to ask even when their class would normally run, since their code is not tomo's. An explicit per-tool rule still wins, so a user who trusts one can allow it.
type Entry ¶
type Entry struct {
Time string `json:"time"`
Tool string `json:"tool"`
Class tool.Class `json:"class"`
Input json.RawMessage `json:"input,omitempty"`
Decision Decision `json:"decision"`
Reason string `json:"reason"`
Approved *bool `json:"approved,omitempty"`
Allowed bool `json:"allowed"`
Tainted bool `json:"tainted"`
}
Entry is one line of the audit log.
type FileAuditor ¶
type FileAuditor struct {
// contains filtered or unexported fields
}
FileAuditor appends one JSON object per line to an audit file. Append-only and flushed per write, so the record survives a crash and cannot be quietly rewritten. A write error is dropped rather than crashing the agent; the audit log must never be the thing that takes tomo down.
func OpenFileAuditor ¶
func OpenFileAuditor(path string) (*FileAuditor, error)
OpenFileAuditor opens (creating, appending) the audit log at path.
type Guard ¶
type Guard struct {
// contains filtered or unexported fields
}
Guard couples the engine with an approver, an auditor, and the session's taint state. It implements the gate the agent loop calls. Safe for the single-turn use the loop makes of it; the mutex guards taint against a channel updating it concurrently.
func (*Guard) Allow ¶
func (g *Guard) Allow(ctx context.Context, name string, class tool.Class, input json.RawMessage) (bool, string)
Allow decides whether a call may run, blocking for approval when the policy says ask. It returns the go-ahead and, when refused, a reason to hand back to the model so it can adapt instead of silently failing.
func (*Guard) Ingested ¶
Ingested updates taint after a tool ran. A successful network call or any external tool result introduces untrusted content, so later writes and exec calls escalate to approval. Externality is keyed by the registered tool name instead of trusting a third party to classify itself as net.