Files
onix/pkg/plugin/implementation/policyenforcer/README.md
Ayush Rawat dfbaf5c6c5 Refactor Policy Enforcer to Use Unified PolicyPaths
- Updated the Policy Enforcer to consolidate policy source configuration under a single `policyPaths` key, allowing for auto-detection of URLs, directories, and files.
- Removed deprecated keys such as `policyFile` and `policyUrls` from the configuration structure.
- Adjusted related code and tests to ensure compatibility with the new configuration format.
- Enhanced documentation to clarify the usage of `policyPaths` and provide examples for various configurations.
2026-03-03 18:49:17 +05:30

3.7 KiB

Policy Enforcer Plugin

OPA/Rego-based policy enforcement for beckn-onix adapters. Evaluates incoming beckn messages against configurable policies and NACKs non-compliant requests.

Overview

The policyenforcer plugin is a Step plugin that:

  • Loads .rego policy files from URLs, local directories, or local files
  • Evaluates incoming messages against compiled OPA policies
  • Returns a BadReqErr (NACK) when policy violations are detected
  • Fails closed on evaluation errors (treats as NACK)
  • Is strictly opt-in — adapters that don't reference it are unaffected

Configuration

All config keys are passed via map[string]string in the adapter YAML config.

Key Required Default Description
policyPaths Yes (at least one source required) ./policies (if dir exists) Comma-separated list of policy sources — each entry is auto-detected as a URL, directory, or file
query No data.policy.violations Rego query returning violation strings
actions No (empty — all actions) Comma-separated beckn actions to enforce. When omitted, all actions are evaluated and the Rego policy itself decides which to gate.
enabled No true Enable/disable the plugin
debugLogging No false Enable verbose logging
any other key No Forwarded to Rego as data.config.<key>

Policy Sources

policyPaths is the single configuration key for all policy sources. Each comma-separated entry is auto-detected as:

  • Remote URL (http:// or https://): fetched via HTTP at startup
  • Local directory: all .rego files loaded (_test.rego excluded)
  • Local file: loaded directly
# Single directory
config:
  policyPaths: "./policies"

# Single remote URL
config:
  policyPaths: "https://policies.example.com/compliance.rego"

# Mix of URLs, directories, and files
config:
  policyPaths: "https://policies.example.com/compliance.rego,./policies,/local/safety.rego"

When specifying many sources, use the YAML folded scalar (>-) to keep the config readable:

config:
  policyPaths: >-
    https://policies.example.com/compliance.rego,
    https://policies.example.com/safety.rego,
    ./policies,
    /local/overrides/rate-limit.rego

The >- folds newlines into spaces, so the value is parsed as a single comma-separated string.

Minimal Config

By default, the plugin loads .rego files from ./policies and uses the query data.policy.violations. A zero-config setup works if your policies are in the default directory:

policyEnforcer:
  id: policyenforcer
  config: {}

Or specify a custom policy location:

policyEnforcer:
  id: policyenforcer
  config:
    policyPaths: "./policies/compliance.rego"

Air-Gapped Deployments

For environments without internet access, use local file paths or volume mounts:

config:
  policyPaths: "/mounted-policies/compliance.rego,/mounted-policies/safety.rego"

Example Config

plugins:
  policyEnforcer:
    id: policyenforcer
    config:
      policyPaths: >-
        /local/policies/,
        https://policies.example.com/compliance.rego
      minDeliveryLeadHours: "4"
      debugLogging: "true"
steps:
  - policyEnforcer
  - addRoute

Relationship with Schema Validator

policyenforcer and schemavalidator/schemav2validator are separate plugins with different responsibilities:

  • Schema Validator: Validates message structure against OpenAPI/JSON Schema specs
  • Policy Enforcer: Evaluates business rules via OPA/Rego policies

They use different plugin interfaces (SchemaValidator vs Step), different engines, and different error types. Configure them side-by-side in your adapter config as needed.