policy

package
v1.15.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package policy implements fold's deny-by-default allowlist engine: first matching rule allows, otherwise the default decision applies. Policy governs named invocations (tools/call, prompts/get) and filters list results per principal — callers never see tools they cannot call.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MatchSubjects added in v1.8.0

func MatchSubjects(s *config.PolicySubjects, p *auth.Principal) bool

MatchSubjects reports whether a principal satisfies a subject selector.

Exported so tenancy can say "these callers" the same way policy does. There is deliberately one definition of that: a second matcher would drift, and the two would eventually disagree about who a rule covers — which for tenancy would mean assigning someone another tenant's allowance.

Types

type AnnotationSource added in v1.12.0

type AnnotationSource func() (ToolAnnotations, bool)

AnnotationSource yields the annotations of the tool being decided, or false when fold cannot establish them from the snapshot it holds. False denies: a gate that cannot see what it is gating must not wave things through, and failing here in the direction the rest of the engine fails makes the limitation visible the first time someone hits it.

It is a function so that nothing is looked up for a decision that never reaches a rule carrying toolKind.

type ArgSource added in v1.12.0

type ArgSource func() (map[string]any, bool)

ArgSource yields the invocation's parsed arguments, or false when there are none or they could not be parsed. It is a function rather than a map so that nothing is unmarshalled for a call that never reaches a rule carrying constraints — fold forwards arguments as raw JSON and never parses them otherwise, which is part of why the proxy path is allocation-light.

type Decision

type Decision struct {
	Allowed bool
	RuleID  string // matching rule id, if any

	// MaxItems is the matching rule's list cap, 0 for none. It rides on the
	// decision because the caller filtering a list already has the decision in
	// hand: asking the engine a second question per item, to learn a bound
	// that never changes, would be work on the path this project measures.
	MaxItems int

	// FailedArg is the dotted path of the argument constraint that refused an
	// otherwise-matching grant, empty when none did. It exists so a denial can
	// tell a caller *which* condition they missed without disclosing what the
	// rule wanted it to be.
	FailedArg string

	// MissingScopes names the scopes a caller would have needed for a rule
	// that otherwise granted this exact invocation, empty when no rule came
	// that close. A denial is more useful when it says what would fix it, and
	// an agent that reads this can re-authorize for precisely what it lacks
	// instead of retrying blind.
	//
	// Two disclosure rules bound it, both of which matter more than the
	// feature: only scopes the caller *lacks* appear, so re-authorizing adds
	// to what they hold rather than replacing it; and a rule contributes only
	// when scopes were the sole obstacle *and* the rule targets this very
	// server, method, and name. A shortfall reported for a rule the caller
	// failed on other grounds would disclose that a rule exists for something
	// they cannot reach.
	MissingScopes []string
}

Decision is the outcome of a policy check.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine evaluates policy rules.

func New

func New(cfg *config.Policy) *Engine

New builds an engine from configuration. A nil config yields an allow-all engine (policy absent = allow-all, matching fold).

func (*Engine) Decide

func (e *Engine) Decide(p *auth.Principal, upstreamID, method, name string) Decision

Decide checks whether principal may invoke method (e.g. "tools/call") with the un-namespaced name on the given upstream. Protocol plumbing (ping, lists themselves) is not policy-gated; list filtering plus call denial is the enforcement pair.

func (*Engine) DecideCall added in v1.12.0

func (e *Engine) DecideCall(p *auth.Principal, upstreamID, method, name string, ev Evidence) Decision

DecideCall is Decide for an actual invocation: argument constraints must hold, and annotations must be establishable if a rule gates on them. Neither source is consulted unless a rule reaches for it, so a document using neither parses and looks up nothing.

func (*Engine) DecideList added in v1.12.0

func (e *Engine) DecideList(p *auth.Principal, upstreamID, method, name string, tool AnnotationSource) Decision

DecideList is Decide for one item of a list, where the tool is in hand and its annotations can therefore be judged, but there are no arguments to judge.

func (*Engine) DecideServerInitiated added in v1.12.0

func (e *Engine) DecideServerInitiated(p *auth.Principal, upstreamID, method string) Decision

DecideServerInitiated checks whether upstreamID may make a server-initiated request — "sampling/createMessage", "elicitation/create" — of principal's client over a bridged session. It is the reverse of Decide: the upstream is asking, and the principal is who would pay for the answer.

There is nothing to name in either request, so the rule matched is server-and-method only. A rule that grants every method on a server does cover these too: an empty name matches the "*" glob, which is the reading a blanket grant deserves.

func (*Engine) NeedsAnnotations added in v1.12.0

func (e *Engine) NeedsAnnotations() bool

NeedsAnnotations reports whether any rule gates on tool annotations, so the gateway can skip building an AnnotationSource — and skip warming a list to answer it — for the documents that do not.

func (*Engine) NeedsArguments added in v1.12.0

func (e *Engine) NeedsArguments() bool

NeedsArguments reports whether any rule constrains arguments. The gateway asks before building an ArgSource at all.

func (*Engine) Visible

func (e *Engine) Visible(p *auth.Principal, upstreamID, invokeMethod, name string) bool

Visible reports whether a listed item (a tool for "tools/call", a prompt for "prompts/get") should appear in list results for principal.

type Evidence added in v1.12.0

type Evidence struct {
	Args ArgSource
	Tool AnnotationSource
}

Evidence is what a decision may consult beyond names — the invocation's arguments and the tool's annotations. Both are lazy, and either may be nil when the caller has none to offer.

type ToolAnnotations added in v1.12.0

type ToolAnnotations struct {
	ReadOnly    bool
	Destructive bool
}

ToolAnnotations is what a toolKind rule needs to know about a tool. The fields carry the MCP spec's defaults for an unannotated tool, which are fail-safe as written and which fold does not "improve": readOnlyHint defaults to false and destructiveHint to true, so an unannotated tool is not read-only and is destructive.

Jump to

Keyboard shortcuts

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