Documentation
¶
Overview ¶
Package pdp implements the Policy Decision Point for local policy evaluation. It provides bundle fetching, OPA-based evaluation, and background refresh management.
Index ¶
- Constants
- type BundleClient
- type BundleClientOption
- type BundleContents
- type BundleManager
- type BundleManagerOption
- type OPALocalClient
- func (c *OPALocalClient) BundleAge() time.Duration
- func (c *OPALocalClient) Evaluate(ctx context.Context, req *pip.DecisionRequest) (*pip.DecisionResponse, error)
- func (c *OPALocalClient) HasBundle() bool
- func (c *OPALocalClient) LoadBundle(ctx context.Context, modules map[string]string, data map[string]interface{}) error
- type OPALocalOption
Constants ¶
const ( // DefaultPollInterval is the base interval between bundle refresh attempts. DefaultPollInterval = 30 * time.Second // DefaultMaxAge is the maximum acceptable bundle age before it's considered stale. DefaultMaxAge = 10 * time.Minute )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BundleClient ¶
type BundleClient struct {
// contains filtered or unexported fields
}
BundleClient pulls OPA policy bundles from the capiscio-server bundle endpoint.
func NewBundleClient ¶
func NewBundleClient(bundleURL, apiKey string, opts ...BundleClientOption) (*BundleClient, error)
NewBundleClient creates a new bundle pull client. The bundleURL should include the full path, e.g. "https://api.capisc.io/v1/bundles/{workspace_id}". The apiKey is sent as X-Capiscio-Registry-Key header.
func (*BundleClient) BundleURL ¶
func (bc *BundleClient) BundleURL() string
BundleURL returns the configured bundle endpoint URL.
func (*BundleClient) Fetch ¶
func (bc *BundleClient) Fetch(ctx context.Context) (*BundleContents, error)
Fetch pulls the current bundle from the server. Returns the parsed bundle contents or an error if the fetch fails. A nil bundle with a nil error is NOT a valid return — always returns one or the other.
type BundleClientOption ¶
type BundleClientOption func(*BundleClient)
BundleClientOption configures a BundleClient.
func WithBundleHTTPClient ¶
func WithBundleHTTPClient(c *http.Client) BundleClientOption
WithBundleHTTPClient sets a custom HTTP client for the bundle client. A nil value is ignored.
func WithBundleLogger ¶
func WithBundleLogger(l *slog.Logger) BundleClientOption
WithBundleLogger sets the logger for the bundle client. A nil value is ignored.
type BundleContents ¶
type BundleContents struct {
Modules map[string]string `json:"modules"` // filename → Rego source
Data map[string]interface{} `json:"data"` // OPA data document
Revision string `json:"revision,omitempty"` // content-addressable revision hash
}
BundleContents holds the compiled policy bundle from the capiscio-server. This mirrors the server's bundle format to allow in-process OPA evaluation.
type BundleManager ¶
type BundleManager struct {
// contains filtered or unexported fields
}
BundleManager handles background polling, hot-swapping, and staleness detection for OPA policy bundles. It coordinates BundleClient (pull) and OPALocalClient (evaluate).
Staleness behavior per enforcement mode (PM-mandated):
- EM-OBSERVE: Stale bundles are evaluated; violations logged, never blocked.
- EM-GUARD: Stale bundles ARE still evaluated (allow with warning).
- EM-STRICT: Stale bundles are discarded; all requests denied until fresh bundle loaded.
func NewBundleManager ¶
func NewBundleManager(client *BundleClient, evaluator *OPALocalClient, opts ...BundleManagerOption) *BundleManager
NewBundleManager creates a new bundle refresh manager. The manager coordinates periodic bundle fetching (via BundleClient) and hot-swapping into the local evaluator (via OPALocalClient).
func (*BundleManager) Evaluate ¶
func (m *BundleManager) Evaluate(ctx context.Context, req *pip.DecisionRequest) (*pip.DecisionResponse, error)
Evaluate runs a policy decision through the local OPA evaluator and applies bundle staleness checks per enforcement mode (RFC-005 Appendix B §B.4).
Staleness is checked after OPA evaluation succeeds — this preserves the distinction between PDP failure (error return) and stale-but-functional data (synthetic DENY). If the bundle is stale:
- EM-STRICT: returns a synthetic DENY with error_code BUNDLE_STALE.
- EM-OBSERVE/EM-GUARD/EM-DELEGATE: emits TelemetryBundleStale, returns the OPA result.
This implements pip.PDPClient so it can be used directly by the PEP gateway.
func (*BundleManager) IsStale ¶
func (m *BundleManager) IsStale() bool
IsStale reports whether the current bundle has exceeded the max age threshold. Returns true if no bundle is loaded.
func (*BundleManager) RefreshNow ¶
func (m *BundleManager) RefreshNow(ctx context.Context) error
RefreshNow triggers an immediate bundle fetch and load, outside the polling loop. Returns an error if the fetch or load fails.
func (*BundleManager) Start ¶
func (m *BundleManager) Start(ctx context.Context)
Start begins background bundle polling. Returns immediately. Call Stop to terminate the polling goroutine.
func (*BundleManager) Stop ¶
func (m *BundleManager) Stop()
Stop terminates background polling. Safe to call multiple times.
type BundleManagerOption ¶
type BundleManagerOption func(*BundleManager)
BundleManagerOption configures a BundleManager.
func WithEnforcementMode ¶
func WithEnforcementMode(em pip.EnforcementMode) BundleManagerOption
WithEnforcementMode sets the enforcement mode for staleness behavior.
func WithManagerLogger ¶
func WithManagerLogger(l *slog.Logger) BundleManagerOption
WithManagerLogger sets the logger for the bundle manager.
func WithMaxAge ¶
func WithMaxAge(d time.Duration) BundleManagerOption
WithMaxAge sets the maximum acceptable bundle age before staleness.
func WithPollInterval ¶
func WithPollInterval(d time.Duration) BundleManagerOption
WithPollInterval sets the base polling interval.
type OPALocalClient ¶
type OPALocalClient struct {
// contains filtered or unexported fields
}
OPALocalClient implements pip.PDPClient using an embedded OPA evaluator. It evaluates policy decisions in-process using Rego modules and data pulled from the capiscio-server bundle endpoint.
Thread-safety: Evaluate takes a read lock, LoadBundle takes a write lock. Multiple concurrent evaluations proceed without blocking each other.
func NewOPALocalClient ¶
func NewOPALocalClient(opts ...OPALocalOption) *OPALocalClient
NewOPALocalClient creates a new local OPA evaluator. The client starts without a loaded policy — call LoadBundle before evaluating.
func (*OPALocalClient) BundleAge ¶
func (c *OPALocalClient) BundleAge() time.Duration
BundleAge returns the duration since the last bundle was loaded. Returns 0 if no bundle has been loaded.
func (*OPALocalClient) Evaluate ¶
func (c *OPALocalClient) Evaluate(ctx context.Context, req *pip.DecisionRequest) (*pip.DecisionResponse, error)
Evaluate sends a PIP decision request through the local OPA evaluator. Returns an error if no bundle is loaded (the PEP handles this per enforcement mode).
func (*OPALocalClient) HasBundle ¶
func (c *OPALocalClient) HasBundle() bool
HasBundle reports whether a policy bundle is currently loaded.
func (*OPALocalClient) LoadBundle ¶
func (c *OPALocalClient) LoadBundle(ctx context.Context, modules map[string]string, data map[string]interface{}) error
LoadBundle compiles Rego modules and data into a prepared query. This takes a write lock and atomically replaces the current prepared query. Callers should use BundleContents from a successful BundleClient.Fetch.
type OPALocalOption ¶
type OPALocalOption func(*OPALocalClient)
OPALocalOption configures an OPALocalClient.
func WithOPALogger ¶
func WithOPALogger(l *slog.Logger) OPALocalOption
WithOPALogger sets the logger for the OPA evaluator.