enforce

package
v0.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package enforce is the policy decision-point client: it signs a CheckRequest and POSTs it to prism's /v1/flyedge/check, returning a typed Decision. The JSON shapes here are the frozen wire schema shared with prism/policy-enforcer and the Python/TS SDKs.

Index

Constants

View Source
const (
	// HeaderOBOPrincipal carries the on-behalf-of envelope as base64url(JSON) with NO padding
	// (prism decodes URL_SAFE_NO_PAD). prism extracts provider/upn (+ urn/scope) for governance.
	HeaderOBOPrincipal = "X-CompFly-OBO-Principal"
	// HeaderDelegationToken carries a compact EdDSA JWT (typ "delegation-token"); prism forwards it
	// to the token-service to verify and unpacks the mandate chain. The SDK just attaches the token.
	HeaderDelegationToken = "X-CompFly-Delegation-Token"
	// HeaderAgentSID is the non-human-identity subject id (plain string).
	HeaderAgentSID = "X-CompFly-Agent-SID"
	// HeaderAgentURN is the structured NHI urn (plain string), grammar
	// "compfly:identity:v1:<provider>:<id_type>:<k>=<v>,...".
	HeaderAgentURN = "X-CompFly-Agent-URN"
)

Identity-attribution headers prism reads on /v1/flyedge/check. These are NOT validated credentials on their own — prism trusts them on the strength of the DID-signed channel (the Ed25519 signature covers the body + timestamp, not the header set); the real credential is the raw token in the request body. The names + value formats are the frozen wire contract, verified against prism.

View Source
const (
	OriginTypeUser       = "user"       // direct user request (human in the loop)
	OriginTypeAgent      = "agent"      // agent-mediated request (default)
	OriginTypeAutonomous = "autonomous" // fully autonomous, no user context
)

OriginType values for CheckRequest.OriginType (prism FlyedgeOriginType, snake_case). Use one of these — prism rejects any other value (it deserializes into a fixed enum).

Variables

This section is empty.

Functions

func ContextWithAgentIdentity

func ContextWithAgentIdentity(ctx context.Context, sid, urn string) context.Context

ContextWithAgentIdentity returns a context carrying the agent's non-human-identity attribution: sid (subject id) and/or urn (structured identity). Both empty is ignored. These ride alongside the crypto DID (set by the signer) as extra attribution for the acting agent.

func ContextWithDelegation

func ContextWithDelegation(ctx context.Context, token string) context.Context

ContextWithDelegation returns a context carrying a raw delegation-token JWT (agent-to-agent authority). The enforcer attaches it as the delegation header; prism verifies it and unpacks the intent/task mandate chain encoded inside the token. Empty token is ignored.

func ContextWithEndpointAgent

func ContextWithEndpointAgent(ctx context.Context, ea EndpointAgent) context.Context

ContextWithEndpointAgent returns a context carrying the endpoint-agent identity a sensor observes for this operation. Check merges it onto the request's EndpointAgent when the caller didn't set one, so a sensor sets it per event and prism resolves the exact instance. The zero value is ignored.

func ContextWithPrincipal

func ContextWithPrincipal(ctx context.Context, p Principal) context.Context

ContextWithPrincipal returns a context carrying the end-user an agent is acting on behalf of. The enforcer attaches it as the OBO header on every signed request made with this context. The zero Principal is ignored. This is the mechanism a single served agent uses to govern per-user: set the principal for the served request, and the gateway sees who each action is for.

func ContextWithTraceparent

func ContextWithTraceparent(ctx context.Context, traceparent string) context.Context

ContextWithTraceparent attaches a W3C `traceparent` so the signed POSTs carry it. prism reads it (field[1]=trace id, field[2]=parent span) to place the check in its lifecycle span tree. Empty is ignored.

func IdentityHeaders

func IdentityHeaders(ctx context.Context) map[string]string

IdentityHeaders returns the identity-attribution headers carried on ctx, ready to attach to a signed request. Empty map when none are set. These are headers only — never part of the signed body — so they never disturb the frozen /check request schema or its signature.

Types

type Action

type Action string

Action is the normalized decision. Block folds into Deny for callers; Warn is advisory.

const (
	ActionAllow Action = "allow"
	ActionDeny  Action = "deny"
	ActionWarn  Action = "warn"
)

type AuthContext

type AuthContext struct {
	Method            string   `json:"method,omitempty"`
	UserGroups        []string `json:"user_groups,omitempty"`
	Department        string   `json:"department,omitempty"`
	ClearanceLevel    string   `json:"clearance_level,omitempty"`
	LastAuthMinutes   *int64   `json:"last_auth_minutes,omitempty"`
	FailedAttempts    *int     `json:"failed_attempts,omitempty"`
	DeviceID          string   `json:"device_id,omitempty"`
	DeviceTrustScore  *float32 `json:"device_trust_score,omitempty"`
	SessionAgeMinutes *int64   `json:"session_age_minutes,omitempty"`
	RequiresAudit     bool     `json:"requires_audit,omitempty"`
	DataResidency     string   `json:"data_residency,omitempty"`
}

AuthContext carries application auth attributes for attribute-based governance (prism FlyedgeAuthContext). Optional; pointers distinguish unset from zero.

type CheckRequest

type CheckRequest struct {
	RequestID     string    `json:"request_id"`
	SessionID     string    `json:"session_id"`
	TimestampMS   int64     `json:"timestamp_ms"`
	Stage         Stage     `json:"stage"`
	ComponentType string    `json:"component_type"` // e.g. LLM, TOOL
	ComponentName string    `json:"component_name"` // e.g. ChatAnthropic
	MethodName    string    `json:"method_name"`    // e.g. invoke
	Content       Content   `json:"content"`
	Operation     Operation `json:"operation"`
	// Enrichment context prism accepts and feeds to policy. All optional — unset
	// fields fall back to prism's serde defaults. Framework identifies the SDK.
	Framework        string            `json:"framework,omitempty"`
	Layer            string            `json:"layer,omitempty"`
	Provider         string            `json:"provider,omitempty"`
	OriginType       string            `json:"origin_type,omitempty"`
	ExecutionContext *ExecutionContext `json:"execution_context,omitempty"`
	AuthContext      *AuthContext      `json:"auth_context,omitempty"`
	EndpointAgent    *EndpointAgent    `json:"endpoint_agent,omitempty"`
	Metadata         map[string]any    `json:"metadata,omitempty"`
}

CheckRequest is the body POSTed to /v1/flyedge/check. The signature is computed over the exact serialized bytes of this struct, so serialize once and sign those bytes.

type Content

type Content struct {
	Preview   string `json:"preview"`
	Full      string `json:"full"`
	Hash      string `json:"hash"`
	SizeBytes int    `json:"size_bytes"`
}

Content is the payload under inspection. Full carries the text; Hash + SizeBytes are required by the gateway and filled automatically from Full when unset (see CheckRequest.fillDefaults). Preview is a short excerpt for logs.

type Decision

type Decision struct {
	Action        Action
	Reason        string
	Message       string
	Warnings      []string
	PolicyVersion string
	RequestID     string
	Signals       Signals
	Kills         []KillInfo // non-full-scope kills matching this request (model/tool)
}

Decision is the typed, normalized result the caller sees.

type EndpointAgent

type EndpointAgent struct {
	InstanceKey string `json:"instance_key,omitempty"` // canonical product + workspace-root identity
	EndpointID  string `json:"endpoint_id,omitempty"`  // durable device id (the sensor install)
	ProductKey  string `json:"product_key,omitempty"`  // normalized agent, e.g. "claude-code"
	WorkContext string `json:"work_context,omitempty"` // display/evidence repository context
}

EndpointAgent carries endpoint-agent context observed by a sensor. InstanceKey is the canonical enforcement handle: it is signed in the check body and paired by Prism with the verified sensor DID and the attested product key. The remaining fields are inventory context only.

func EndpointAgentFromContext

func EndpointAgentFromContext(ctx context.Context) (EndpointAgent, bool)

EndpointAgentFromContext returns the endpoint-agent identity set via ContextWithEndpointAgent, and whether one was present.

type Enforcer

type Enforcer interface {
	Check(ctx context.Context, req CheckRequest) (Decision, error)
}

Enforcer is the policy decision point. Implementations are swappable (HTTP client, an offline stub for tests, a record/replay fake). Check must be safe for concurrent use.

type ExecutionContext

type ExecutionContext struct {
	Environment  string `json:"environment,omitempty"`
	IsAutonomous bool   `json:"is_autonomous,omitempty"`
	TriggerType  string `json:"trigger_type,omitempty"`
	Scheduled    bool   `json:"scheduled,omitempty"`
	EventDriven  bool   `json:"event_driven,omitempty"`
}

ExecutionContext describes how/where the operation runs (prism FlyedgeExecutionContext) — feeds environment/autonomy/trigger-aware policy. Optional.

type HTTPEnforcer

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

HTTPEnforcer calls prism /v1/flyedge/check over HTTP, signing the request body with a Signer.

func NewHTTPEnforcer

func NewHTTPEnforcer(baseURL string, signer identity.Signer, timeout time.Duration) *HTTPEnforcer

NewHTTPEnforcer builds an enforcer. baseURL is the prism base (no trailing slash needed); signer may be nil; timeout bounds each call.

func (*HTTPEnforcer) Check

func (e *HTTPEnforcer) Check(ctx context.Context, req CheckRequest) (Decision, error)

Check serializes req, signs the exact bytes, POSTs to /v1/flyedge/check, and returns the typed Decision. A non-2xx or transport error is returned as an error — the caller (Guard) decides fail-open vs fail-closed; this layer does not silently allow.

func (*HTTPEnforcer) GetSigned

func (e *HTTPEnforcer) GetSigned(ctx context.Context, path string, headers map[string]string) ([]byte, error)

GetSigned signs and GETs path (empty body), attaching extra request headers (e.g. the X-Agent-* heartbeat headers on /v1/flyedge/config). The signature is computed over the empty body, matching prism's SHA-256(ts‖body) scheme for a body-less request. A non-2xx is an error.

func (*HTTPEnforcer) GetSignedConditional

func (e *HTTPEnforcer) GetSignedConditional(ctx context.Context, path string, headers map[string]string) ([]byte, bool, error)

GetSignedConditional is GetSigned with 304 handling: a Not Modified response comes back as (nil, true, nil) rather than an error. Plain GetSigned treats every non-2xx as a failure, which is right for endpoints where 304 is meaningless — this variant exists for poll endpoints that send If-None-Match and want "nothing changed" as an ordinary outcome, not an error to swallow.

func (*HTTPEnforcer) PostSigned

func (e *HTTPEnforcer) PostSigned(ctx context.Context, path string, body []byte) ([]byte, error)

PostSigned signs and POSTs body to an arbitrary flyedge path (e.g. /v1/flyedge/connect, /v1/flyedge/telemetry), returning the response bytes. Reuses the same signing as Check so the connect + telemetry lifecycle calls authenticate identically. A non-2xx is an error.

type KillInfo

type KillInfo struct {
	KillID string `json:"kill_id"`
	Scope  string `json:"scope"` // full | model | tool | provider
	Target string `json:"target"`
	Reason string `json:"reason"`
}

KillInfo describes an active kill switch matching a request. Full-scope kills arrive as a 403 (see KilledError); non-full (model/tool) kills arrive in a 200 response's `kills` array.

type KilledError

type KilledError struct{ Kill KillInfo }

KilledError is returned by Check when the gateway rejects a request with a full-scope kill switch (HTTP 403 code=KILL_SWITCH). It is distinct from a policy deny so the Guard can enforce it unconditionally (a kill must never be bypassed by fail-open).

func (*KilledError) Error

func (e *KilledError) Error() string

type Operation

type Operation struct {
	Type         string `json:"type"`
	ToolName     string `json:"tool_name,omitempty"`
	ToolArgsHash string `json:"tool_args_hash,omitempty"`
	// ToolArgsJSON carries the full tool arguments as JSON so argument-level policies can evaluate
	// values (prism forwards it to the enforcer's tool_args map). Distinct from Content: Content is
	// the inspected/hashed payload + preview; this is the structured args for policy.
	ToolArgsJSON string `json:"tool_args_json,omitempty"`
	ModelID      string `json:"model_id,omitempty"`
	DestDomain   string `json:"dest_domain,omitempty"`
	MCPServerID  string `json:"mcp_server_id,omitempty"`
}

Operation describes what the agent is doing (chat completion, tool call, …).

type Principal

type Principal struct {
	Provider string `json:"provider,omitempty"`
	TenantID string `json:"tenantId,omitempty"`
	OBOID    string `json:"oboId,omitempty"`
	UPN      string `json:"upn,omitempty"`
	URN      string `json:"urn,omitempty"`
	// Scope is the principal's claim map (role, plan, tenant, groups, …) — the attribute bag policy
	// keys on. It is sent as a JSON OBJECT (prism serializes it into obo_scope_json for the policy
	// engine), NOT a pre-encoded string, so a rule reads obo.scope.<claim> directly.
	Scope       map[string]string `json:"scope,omitempty"`
	TokenHash   string            `json:"tokenHash,omitempty"`
	Issuer      string            `json:"issuer,omitempty"`
	Algorithm   string            `json:"algorithm,omitempty"`
	KeyID       string            `json:"keyId,omitempty"`
	PresentedAt string            `json:"presentedAt,omitempty"`
	ExpiresAt   string            `json:"expiresAt,omitempty"`
}

Principal is the on-behalf-of envelope: the end-user an agent is acting for on a given request. It maps to prism's OBO header (base64url JSON). prism plucks Provider/UPN (+ URN/Scope) for policy; the remaining fields are audit metadata. The envelope is an extraction hint — the actual credential is the raw OBO token in the request body — so a full production deployment sets both this and the underlying token. Field names match the platform envelope (camelCase).

type Signals

type Signals struct {
	Present []string
	Missing []string
}

Signals carries the capability signals the server reported present/missing (audit trail).

type Stage

type Stage string

Stage is the enforcement point in an agent turn. Values are wire strings — do not change.

const (
	StagePreLLM           Stage = "pre_llm"
	StageToolCall         Stage = "tool_call"
	StageToolCallResponse Stage = "tool_call_response"
	StagePostLLM          Stage = "post_llm"
)

Jump to

Keyboard shortcuts

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