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

enforce

package
v1.0.1 Latest Latest
Warning

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

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

Documentation

Overview

Package enforce is the harness's refusal seam: it sits on the proxy's interception hook, decides each decidable client request against the policy engine, and — on a denial — synthesizes the refusal in the exact shape the method requires, so the governed server never sees a request the harness refused.

This is the enforcing half of moving adjudication out of the server. The verdict is reached in the harness process, on the wire, using the tool index the observer built from the manifest; the server is not consulted and cannot override it. Two shape rules are load-bearing and match the in-process enforcer exactly: a refused tools/call is an IsError result (a JSON-RPC success frame carrying isError), so the model can read the reason and adapt; a refused resources/read, prompts/get, completion/complete or subscriptions/listen is a JSON-RPC error, because those methods have no IsError envelope and a CallToolResult would be the wrong shape on the wire.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AllowAll added in v0.4.0

type AllowAll struct{}

AllowAll forwards every decidable request. It is the inner decider behind a manifest gate when no policy layers are present: the manifest bounds the session while the policy stack has nothing to say.

func (AllowAll) Decide added in v0.4.0

Decide always forwards.

type Decider

type Decider interface {
	Decide(ctx context.Context, method string, params json.RawMessage) Decision
}

Decider reaches a verdict on one decidable request. It is the policy engine (behind the manifest gate) in production and a fake in tests.

func NewManifestGate added in v0.4.0

func NewManifestGate(
	s *manifest.Session,
	inner Decider,
	a *audit.AuditLog,
	logger *slog.Logger,
	now func() time.Time,
) Decider

NewManifestGate wraps the inner decider with the session's bounds. A nil session gates nothing and returns the inner decider unchanged, so callers can compose unconditionally.

type Decision added in v0.4.0

type Decision struct {
	Allow  bool
	Code   string
	Reason string
}

Decision is a decider's answer for one decidable request. Code, when set, is a typed refusal code from the wire package — the machine-readable half of a manifest or session-lifetime refusal. Policy-rule denials carry only a reason (the signal and rule that failed explain them); the empty code keeps their refusal text exactly as the in-process enforcer renders it.

func Allowed added in v0.4.0

func Allowed() Decision

Allowed is the decision that forwards a request.

func Refused added in v0.4.0

func Refused(code, reason string) Decision

Refused builds a typed refusal decision.

type DenyAll

type DenyAll struct{ Reason string }

DenyAll refuses every decidable request with a fixed reason. It is the fail-closed decider installed while the policy engine is still initializing (before the servant has connected and its signals are reachable): an operator who configured enforcement must not get an unadjudicated window, so decidable calls are refused until the real decider is ready. Non-decidable traffic — initialize, tools/list, notifications — is unaffected, because the Interceptor never consults the decider for those.

func (DenyAll) Decide

Decide always refuses.

type Interceptor

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

Interceptor implements proxy.Interceptor over a Decider. It only ever refuses the five decidable request methods; everything else it forwards untouched by returning nil.

func NewInterceptor

func NewInterceptor(d Decider, logger *slog.Logger) *Interceptor

NewInterceptor builds the refusal seam.

func (*Interceptor) Intercept

func (i *Interceptor) Intercept(raw []byte) []byte

Intercept decides a client frame. A decidable request with a readable id is put to the decider; a denial is answered in the method's shape and the request is dropped. Everything else returns nil (forward).

type ManifestGate added in v0.4.0

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

ManifestGate enforces the session manifest ahead of the inner decider: the two session clocks first (an out-of-time session drains with a typed refusal, whatever the request), then the grant lists for the method, then the wrapped policy decision. The idle clock is touched only for requests the whole chain allowed — a refused call must not keep a session alive.

The gate enforces whenever a manifest is present, regardless of policy mode: a manifest has no audit mode, it is a grant, and a grant that only logged its bounds would not bound anything.

func (*ManifestGate) Decide added in v0.4.0

func (g *ManifestGate) Decide(ctx context.Context, method string, params json.RawMessage) Decision

Decide applies clocks, grant, then the inner decider.

type PolicyDecider

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

PolicyDecider adapts the rule-layer engines to the Decider interface. It builds the subject for the method from the request params — using the tool index for a tools/call, and the read-only data-egress subject for the other four, matching the in-process enforcer's subjectFor — evaluates every layer, and takes the strictest verdict (manifest.MaxVerdict): rules are never merged into one attribution space, per the layering semantics. In audit mode the engines cap severity below deny, so Allowed() is true and the call forwards while the intended verdict is still recorded: the harness observes without refusing, exactly as a standalone server does under an audit policy.

func NewPolicyDecider

func NewPolicyDecider(engines []*policy.Engine, a *audit.AuditLog, logger *slog.Logger) *PolicyDecider

NewPolicyDecider builds the engine-backed decider over the rule layers, in composition order. With no engines every request is allowed: no layers, no rules.

func (*PolicyDecider) Decide

func (d *PolicyDecider) Decide(ctx context.Context, method string, params json.RawMessage) Decision

Decide evaluates the request against every layer and records the strictest verdict.

Jump to

Keyboard shortcuts

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