approval

package
v0.0.0-...-3613e4e Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package approval provides a transport-agnostic approval bridge for pausing framework operations until a host explicitly allows or denies them.

Canto owns the durable approval state machine, policy composition, and circuit-breaker plumbing. Hosts own product policy: which tools, arguments, users, paths, or command strings are safe enough to allow automatically. Use PolicyFunc for local deterministic policies, such as a command classifier in an interactive host. Return handled=false to leave the request for HITL resolution instead of forcing an automated allow or deny.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrRequestNotFound = errors.New("approval request not found")
	ErrRequestResolved = errors.New("approval request already resolved")
	ErrInvalidDecision = errors.New("invalid approval decision")
)

Functions

func DefaultClassifierPrompt

func DefaultClassifierPrompt(sess *session.Session, req Request) (string, error)

DefaultClassifierPrompt builds a basic prompt for tool call classification.

Types

type Chain

type Chain []Policy

Chain composes multiple policies in order.

Later policies can override earlier ones by returning handled=true. If no policy handles the request, the chain returns handled=false.

func (Chain) Decide

func (c Chain) Decide(
	ctx context.Context,
	sess *session.Session,
	req Request,
) (Result, bool, error)

Decide implements Policy.

type ClassifierPolicy

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

ClassifierPolicy uses an LLM classifier to make automated approval decisions.

func NewClassifierPolicy

func NewClassifierPolicy(classifier llm.Classifier, labels []string) *ClassifierPolicy

NewClassifierPolicy creates a new policy backed by an LLM classifier.

func (*ClassifierPolicy) Decide

func (p *ClassifierPolicy) Decide(
	ctx context.Context,
	sess *session.Session,
	req Request,
) (Result, bool, error)

Decide implements Policy.

func (*ClassifierPolicy) WithPromptBuilder

func (p *ClassifierPolicy) WithPromptBuilder(
	builder func(*session.Session, Request) (string, error),
) *ClassifierPolicy

WithPromptBuilder configures a custom prompt builder for the classifier.

type Decision

type Decision string
const (
	DecisionAllow Decision = "allow"
	DecisionDeny  Decision = "deny"
)

type Gate

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

Gate coordinates tool approval requests across automated policies and human-in-the-loop (HITL) resolution. It includes a circuit breaker that disables automated policies after N consecutive denials.

func NewGate

func NewGate(policy Policy) *Gate

func (*Gate) IsTripped

func (m *Gate) IsTripped() bool

IsTripped returns true if the circuit breaker has tripped.

func (*Gate) Pending

func (m *Gate) Pending() []string

func (*Gate) Request

func (m *Gate) Request(
	ctx context.Context,
	sess *session.Session,
	toolName string,
	args string,
	requirement Requirement,
) (Result, error)

func (*Gate) ResetBreaker

func (m *Gate) ResetBreaker()

ResetBreaker clears the circuit breaker state.

func (*Gate) Resolve

func (m *Gate) Resolve(requestID string, decision Decision, reason string) error

func (*Gate) WithAuditLogger

func (m *Gate) WithAuditLogger(logger audit.Logger) *Gate

WithAuditLogger configures an append-only security audit logger.

func (*Gate) WithThreshold

func (m *Gate) WithThreshold(n int) *Gate

WithThreshold configures the circuit breaker threshold.

type Policy

type Policy interface {
	Decide(ctx context.Context, sess *session.Session, req Request) (Result, bool, error)
}

func NewChain

func NewChain(policies ...Policy) Policy

NewChain creates a composable policy chain.

type PolicyFunc

type PolicyFunc func(ctx context.Context, sess *session.Session, req Request) (Result, bool, error)

PolicyFunc adapts a function into a Policy.

Example (ShellClassifierSeam)
package main

import (
	"context"
	"fmt"
	"strings"

	"github.com/nijaru/canto/approval"
	"github.com/nijaru/canto/session"
)

func main() {
	// A host can provide this policy without Canto owning the
	// command heuristics. The example is deliberately tiny: real products can
	// parse POSIX shell, check cwd/path policy, and inspect user trust state.
	shellPolicy := approval.PolicyFunc(
		func(
			ctx context.Context,
			sess *session.Session,
			req approval.Request,
		) (approval.Result, bool, error) {
			if req.Tool != "bash" {
				return approval.Result{}, false, nil
			}
			if strings.HasPrefix(req.Resource, "git status") {
				return approval.Result{
					Decision: approval.DecisionAllow,
					Reason:   "host classifier allows read-only git status",
				}, true, nil
			}
			return approval.Result{}, false, nil
		},
	)

	manager := approval.NewGate(shellPolicy)
	sess := session.New("s")
	res, err := manager.Request(
		context.Background(),
		sess,
		"bash",
		`{"command":"git status --short"}`,
		approval.Requirement{
			Category:  "execute",
			Operation: "exec",
			Resource:  "git status --short",
		},
	)

	fmt.Println(res.Decision, res.Automated, err == nil)
}
Output:
allow true true

func (PolicyFunc) Decide

func (f PolicyFunc) Decide(
	ctx context.Context,
	sess *session.Session,
	req Request,
) (Result, bool, error)

Decide implements Policy.

type Request

type Request struct {
	ID        string
	SessionID string
	Tool      string
	Args      string
	Category  string
	Operation string
	Resource  string
	Metadata  map[string]any
}

type Requirement

type Requirement struct {
	Category  string
	Operation string
	Resource  string
	Metadata  map[string]any
}

type Result

type Result struct {
	RequestID string
	Decision  Decision
	Reason    string
	Automated bool // true if decided by policy, false if resolved via Resolve (HITL)
}

func (Result) Allowed

func (r Result) Allowed() bool

func (Result) Error

func (r Result) Error() error

Jump to

Keyboard shortcuts

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