Configuration Reference¶
OAG policies are human-readable and deterministic. The policy model defaults to deny and explicitly allows known-safe destinations, methods, and paths.
Policy Model¶
- Default action: deny.
- Rules are evaluated in a deterministic order.
- Deny rules have priority over allow rules when both match.
- Redirect targets are evaluated as new requests when
--enforce-redirect-policyis enabled. [RFC9110] [RFC7231] - If
enforce_dns_resolutionis enabled, hostnames must resolve to at least one IP before policy evaluation for HTTP requests.
Example Policy¶
version: 1
defaults:
action: deny
max_body_bytes: 1048576
enforce_dns_resolution: false
allow:
- id: openai_api
host: api.openai.com
methods: [POST]
paths: [/v1/*]
secrets: [OPENAI_KEY]
tags: [ai, billing]
- id: github_readonly
host: "*.github.com"
methods: [GET]
paths: [/*]
header_match:
- header: Authorization
present: true
- header: X-Source
value: agent
header_rewrites:
- action: SET
header: X-Source
value: oag-proxy
- action: REMOVE
header: X-Internal-Debug
deny:
- id: cloud_metadata
host: 169.254.169.254
error_response:
status: 451
body: '{"error":"access_denied","message":"Cloud metadata access blocked"}'
content_type: application/json
- id: local_ip_ranges
host: 10.0.0.1
ip_ranges: ["10.0.0.0/24", "fd00::/8"]
secret_scopes:
- id: OPENAI_KEY
hosts: [api.openai.com]
methods: [POST]
paths: [/v1/*]
ip_ranges: ["203.0.113.0/24"]
Rule Fields¶
id: Stable identifier used in audit logs. Must not contain whitespace.host: Exact match or wildcard (*.example.com). Must not start with., must not contain consecutive dots, and must not include a port.methods: Allowed HTTP methods.paths: Prefix or glob match. Paths must start with/or*, must not contain whitespace, and must not include scheme/host.secrets: Secret IDs allowed to materialize for this destination.- Secret IDs must not contain whitespace.
ip_ranges: Optional CIDR ranges (IPv4/IPv6). Only matches when the request host is an IP literal within any listed range.reason_code: Optional custom reason code string. When a rule matches, this value is emitted in audit logs instead of the built-inallowed_by_ruleordenied_by_rule. Must not contain whitespace.rate_limit: Optional rate limiting for matched requests.requests_per_second: Token refill rate (required).burst: Maximum burst size (tokens available immediately, required). Bothrequests_per_secondandburstmust be specified together. When a request matches a rate-limited rule but the token bucket is exhausted, the request is denied with reason coderate_limited.max_body_bytes: Optional override of the global limit for this rule.connect_timeout_ms: Optional per-rule override for the upstream connect timeout (milliseconds). Falls back to the global--connect-timeout-msvalue when not set. Must be greater than 0.read_timeout_ms: Optional per-rule override for the upstream read timeout (milliseconds). Falls back to the global--read-timeout-msvalue when not set. Must be greater than 0.body_match: Optional content matching constraints for request bodies.contains: List of literal strings that must all appear in the body.patterns: List of regex patterns that must all match in the body. Bothcontainsandpatternsuse AND semantics: all entries must match.response_body_match: Optional content matching constraints for upstream response bodies.contains: List of literal strings that must all appear in the response body.patterns: List of regex patterns that must all match in the response body. When a response matches, the audit event recordsresponse_injection_detected. Only applies to fixed-length responses withinmax_response_scan_bytes.skip_response_scanning: Whentrue, disables response body scanning for this rule even ifresponse_body_matchis defined. Useful for temporarily disabling scanning without removing configuration.content_inspection: Per-rule content inspection override. When set, overrides the defaults-levelcontent_inspectionfor requests matching this rule.enable_builtin_patterns: Enable built-in injection pattern library.custom_patterns: List of regex patterns (anchor:any).anchored_patterns: List of patterns with explicit anchor modes. See below.skip_content_inspection: Whentrue, disables content inspection for this rule even if defaults-level content inspection is enabled. Cannot be combined with per-rulecontent_inspection. Per-rulecontent_inspectionalso supportsscan_streaming_responsesto override the defaults-level streaming scan setting for that rule.header_rewrites: Optional list of header rewrite operations applied to the upstream request before forwarding. Each entry specifies:action: One ofSET,REMOVE, orAPPEND.SET: Overwrites or creates the header with the given value.REMOVE: Strips the header from the upstream request. No audit entry is emitted if the header was already absent.APPEND: Appends the value to an existing header (comma-separated), or creates it if absent.
header: The header name. Must not be blank, must not contain whitespace, and must not be a reserved header (Host,Content-Length,Transfer-Encoding,TE,Trailer,Upgrade,Connection,Proxy-Connection).value: The header value (required forSETandAPPEND, ignored forREMOVE). Rewrites are applied after secret materialization. Audit events include aheader_rewritesarray recording each rewrite that was applied.query_match: Optional list of URL query parameter constraints. All entries must match (AND semantics). Each entry specifies:param: Query parameter name (case-sensitive).value: Exact value match (case-insensitive). Mutually exclusive withpatternandpresent.pattern: Regex pattern to match against the parameter value. Mutually exclusive withvalueandpresent.present: Boolean. Whentrue, the parameter must exist. Whenfalse, the parameter must be absent. Mutually exclusive withvalueandpattern.header_match: Optional list of request header constraints. All entries must match (AND semantics). Each entry specifies:header: Header name (case-insensitive matching).value: Exact value match (case-insensitive). Mutually exclusive withpatternandpresent.pattern: Regex pattern to match against the header value. Mutually exclusive withvalueandpresent.present: Boolean. Whentrue, the header must exist. Whenfalse, the header must be absent. Mutually exclusive withvalueandpattern.tags: Optional list of string labels for categorizing matched requests. Tags are emitted in audit events and Prometheus metrics. Must not contain whitespace.error_response: Optional custom error response for denied requests matching this rule.status: HTTP status code (400-599). Defaults to 403 when omitted.body: Response body string (max 8192 characters).content_type: Content-Type header value. Defaults totext/plainwhen omitted. When a deny rule witherror_responsematches, the proxy returns the custom status code and body instead of the default403 Forbiddenwith no body.tls_inspect: Whentrue, enables TLS interception for CONNECT tunnels matching this rule. Requires--tls-inspectat runtime. Decrypted traffic passes through the full body inspection pipeline. See security.md.skip_outbound_credential_detection: Whentrue, disables outbound credential detection for this rule even if enabled at defaults level.data_classification: Per-rule data classification override. Same fields as defaults-leveldata_classification.skip_data_classification: Whentrue, disables data classification for this rule. Cannot be combined with per-ruledata_classification.plugin_detection: Per-rule plugin detection settings. Overrides defaults. See PolicyPluginDetection fields below.skip_plugin_detection: Whentrue, skips plugin detection for this rule. Mutually exclusive withplugin_detection.hallucination_check: Per-rule hallucination check override. Same fields as defaults-levelhallucination_check.skip_hallucination_check: Whentrue, skips hallucination check for this rule. Mutually exclusive withhallucination_check.topic_classification: Per-rule topic classification override. Same fields as defaults-leveltopic_classification.skip_topic_classification: Whentrue, skips topic classification for this rule. Mutually exclusive withtopic_classification.schema_validation: Response schema validation constraints.schema: JSON Schema string to validate response bodies against.on_fail: Action when validation fails:blockorpass.extract_path: RFC 6901 JSON Pointer to extract a sub-element from the response before validation (e.g.,/choices/0/message/content).parse_extracted_json: Whentrue, parse the extracted string as JSON before validating.finding_suppressions: Per-rule finding suppression rules. See PolicyFindingSuppression fields below.response_rewrites: Optional list of response rewrite operations applied before forwarding the response to the client. Each entry specifies:action: One ofREDACT,REMOVE_HEADER, orSET_HEADER.REDACT: Replace matchingpatternin the response body withreplacement.REMOVE_HEADER: Strip a response header.SET_HEADER: Set or overwrite a response header with a given value.
pattern: Regex pattern forREDACTaction.replacement: Replacement string forREDACTaction.header: Header name forREMOVE_HEADERandSET_HEADERactions.value: Header value forSET_HEADERaction.webhook_events: Optional list of webhook event types this rule should trigger notifications for. Omit or empty list to notify on all events. Valid values:circuit_open,reload_failed,injection_detected,credential_detected,integrity_drift,admin_denied,hallucination_detected,schema_validation_failed.payload_match: Optional list of structured payload matching constraints. Allows matching based on protocol-level semantics rather than raw body content. Each entry specifies:protocol: Protocol identifier (e.g.,jsonrpc,graphql).method: Method name to match (protocol-specific).operation: Operation name to match.operation_type: Operation type to match (e.g.,query,mutation,subscriptionfor GraphQL).conditions: Optional block with additional matching constraints. When present, the rule only matches if all conditions are also satisfied.scheme: Required scheme (httporhttps). Case-insensitive.ports: List of allowed destination ports.enforce_dns_resolution: Optional default requiring hostnames to resolve before evaluation for HTTP requests. Resolved IPs are recorded in audit logs.secret_scopes: Optional list of secret scope rules that further restrict when a secret ID may be materialized.
Defaults Fields¶
In addition to action, max_body_bytes, and enforce_dns_resolution, the defaults block supports:
content_inspection: Content inspection configuration.enable_builtin_patterns: Enable built-in injection pattern library (ChatML, INST, instruction override).custom_patterns: List of regex patterns to match against request bodies (implicit anchor:any).anchored_patterns: List of pattern objects with explicit anchor modes:pattern: Regex pattern string.anchor: One ofany(default),start_of_message, orstandalone.any: Match anywhere in the body (same ascustom_patterns).start_of_message: Match only in the first 500 characters.standalone: Match only when the pattern occupies an entire line.
scan_websocket_frames: Enable content inspection of WebSocket text frames for injection patterns and data classification.url_inspection: URL and path security inspection.max_query_length: Maximum query string length before blocking.max_url_length: Maximum total URL length before blocking.max_path_length: Maximum path length before blocking (reason codepath_length_exceeded).block_base64_values: Block query values that look like Base64-encoded data.entropy_threshold: Shannon entropy threshold for query parameter values.min_value_length: Minimum value length before entropy/Base64 analysis applies.path_entropy_threshold: Shannon entropy threshold for path segments.block_path_traversal: Block path traversal patterns (../, encoded variants). Reason codepath_traversal_blocked.block_double_encoding: Block double percent-encoding (%25XX). Reason codedouble_encoding_blocked.block_invalid_percent_encoding: Block requests with invalid percent-encoded sequences (e.g.,%ZZ). Triggersinvalid_percent_encoding_blocked.block_dns_exfiltration: Enable DNS exfiltration detection via subdomain entropy analysis.dns_entropy_threshold: Entropy threshold for DNS labels (default 4.0).dns_min_label_length: Minimum DNS label length for exfiltration detection (default 20).max_response_scan_bytes: Maximum response body size to scan (default 64KB).max_bytes_per_host_per_session: Per-host per-session byte budget for data exfiltration detection.max_tokens_per_session: Maximum cumulative LLM tokens per session before requests are denied withtoken_budget_exceeded.scan_streaming_responses: Enable or disable streaming response scanning for chunked/SSE responses (defaulttrue). Can also be set insidecontent_inspectionfor more granular control.injection_scoring: Heuristic injection scoring configuration.mode:binary(default, deny on any pattern match) orscore(weighted scoring with threshold).deny_threshold: Score threshold above which requests are denied (default 1.0). Only used when mode isscore.log_threshold: Score threshold above which audit logs include scoring details. Must not exceeddeny_threshold.entropy_weight: Weight multiplier for Shannon entropy signal (default 0.1).entropy_baseline: Entropy level below which no contribution is made (default 4.5).category_weights: Optional list of category weight overrides. Each entry hascategory(one ofdelimiter_injection,instruction_override,role_assumption,prompt_leaking,jailbreak,encoding_markers) andweight(non-negative number).escalation: Session-aware escalation detection. Requires--session.enabled: Boolean.window_size: Number of recent scored turns to analyze (3-100, default 5).deny_patterns: List of escalation patterns to detect. Values:sustained_elevation(all scores above threshold),crescendo(strictly increasing scores),saw_tooth_probing(alternating above/below threshold),periodic_testing(high scores at regular intervals).
See security.md for detailed configuration guidance.
ml_classifier: Optional ML-based injection classifier (requires ONNX Runtime on classpath).enabled: Boolean, defaultfalse.model_path: Path to the ONNX model file (required when enabled).tokenizer_path: Path to a HuggingFacetokenizer.jsonfile. When set and DJL HuggingFace Tokenizers is on the classpath, OAG uses proper subword tokenization (WordPiece/BPE). When absent or DJL is unavailable, falls back to raw char-code encoding. The tokenizer must match the ONNX model — switching tokenizers requires a model trained with the same tokenizer.trigger_mode: When to invoke the ML classifier.always(default) runs ML on every request.uncertain_onlyskips ML when the heuristic score is already decisive (belowuncertain_lowor aboveuncertain_high).uncertain_low: Lower bound of the uncertainty band (default 0.3). Heuristic scores below this skip ML.uncertain_high: Upper bound of the uncertainty band (default 0.8). Heuristic scores above this skip ML.confidence_threshold: ML confidence threshold, 0-1 (default 0.5).max_length: Maximum token sequence length (default 512).
See security.md for ML classifier setup and performance guidance.
outbound_credential_detection: Boolean. Whentrue, scan outbound request bodies for leaked credentials (AWS keys, GitHub PATs, Slack tokens, bearer tokens). See security.md.data_classification: Data classification configuration for sensitive data detection.enable_builtin_patterns: Enable built-in sensitive data patterns (financial, credentials, PII).custom_patterns: List of additional regex patterns.categories: List of categories to scan (financial,credentials,pii). Empty = all.scan_responses: Boolean. Whentrue, also scan response bodies.
See security.md for built-in pattern details.
hallucination_check: Hallucination detection for LLM response integrity. See security.md.enabled,mode(observe/enforce),deny_threshold,log_threshold- Signal toggles:
impossible_claims,url_verification,package_verification,logprob_analysis,claim_contradiction,tool_receipt_verification signal_weights: Per-signal weight overrides (nested object with same signal names as fields)- External NLI:
external_endpoint_url,external_endpoint_timeout_ms,external_endpoint_signing_secret,on_timeout(allow/deny) - Custom patterns:
impossible_claims_path,url_verification_allowlist,package_registry_mirror topic_classification: Topic-based request filtering via external classifier. See security.md.enabled,endpoint_url,denied_topicsorallowed_topics(mutually exclusive)confidence_threshold(0-1),endpoint_timeout_ms(max 10000),signing_secret,on_error(deny/allow),max_text_bytesexternal_judge: External judgment endpoint for uncertain injection decisions. See security.md.enabled,endpoint_url,signing_secret,timeout_mstrigger_mode(always/uncertain_only),on_error(deny/allow/skip),deny_threshold(0-1),max_response_bytesplugin_detection: Default plugin detection settings applied to all rules. See PolicyPluginDetection fields below.finding_suppressions: Default finding suppression rules. See PolicyFindingSuppression fields below.
PolicyPluginDetection Fields¶
enabled: Enable plugin detection.detector_ids: Only run these detector IDs. If omitted, all loaded detectors run.exclude_detector_ids: Exclude these detector IDs from running.scan_responses: Also scan response bodies with plugin detectors.deny_severity_threshold: Auto-deny findings at or above this severity (low,medium,high,critical), independent ofRecommendedAction.DENY.
PolicyFindingSuppression Fields¶
detector_id: Suppress findings from this detector ID.finding_type: Suppress findings of this type (e.g.,prompt_injection,credential).pattern: Suppress findings whose evidence matches this regex pattern.hosts: Only suppress for these hosts.
Host Matching¶
- Exact host match for
api.example.com. - Wildcard match for
*.example.com. - Raw IP literals can be denied even if they match a host rule.
Redirect Handling¶
- Redirects are treated as new requests when redirect enforcement is enabled.
- Each redirect target must independently pass policy when redirect enforcement is enabled. Redirect semantics follow HTTP guidance. [RFC9110] [RFC7231] Redirect enforcement applies to HTTP responses only; CONNECT tunneling does not follow redirects.
Retry Configuration¶
Per-rule retry configuration for upstream connection failures:
allow:
- id: openai_api
host: api.openai.com
methods: [POST]
paths: [/v1/*]
retry:
max_retries: 3
retry_delay_ms: 200
Fields:
- max_retries: Maximum number of retry attempts after the initial connection failure. Must be > 0.
- retry_delay_ms: Delay in milliseconds between retry attempts. Must be > 0. Defaults to 100ms if omitted.
Retry only applies to upstream TCP connection failures. Once a request has been sent, failures are not retried. When retries occur, the retry_count field in the audit event records the number of retry attempts made (0 = no retries needed).
Policy Hashing¶
OAG emits a policy hash in audit logs to link decisions to the exact policy version used at runtime.
Policy bundles package policies with policy_hash and optional signatures. See Policy Bundles below.
Policy Includes¶
Policies can reference other policy files using the includes field. Included files are resolved relative to the parent policy file and their allow, deny, and secret_scopes rules are merged into the main policy.
version: 1
includes:
- ./vendor/openai-rules.yaml
- ./deny-lists/cloud-metadata.yaml
defaults:
action: deny
allow:
- id: local_rule
host: api.example.com
methods: [GET]
paths: [/*]
- Include paths are resolved relative to the directory containing the parent policy file.
- Included files may themselves include other files (nested includes).
- Maximum include depth is 3 levels.
- Circular includes are detected and produce an error.
- Missing include files produce an error.
- The
versionanddefaultsfields from included files are ignored; only rules are merged. - Include resolution happens before validation, so merged rules are validated together.
- Policy bundles do not support includes.
Secret Provider Notes (Runtime)¶
Policy only defines secret IDs. Runtime configuration controls how those secrets are materialized:
envprovider readsOAG_SECRET_<ID>by default (override with--secret-prefix).fileprovider reads<id>.secretand optional<id>.secret.versionfrom the secret dir.
Secret files are runtime concerns and do not change policy evaluation.
Policy Bundles¶
Policy bundles package a policy document with metadata, a policy hash, and an optional signature. Bundles are designed for governance workflows where policies are generated, signed, and distributed as a single artifact.
Bundle Format¶
Bundle files are JSON or YAML with the following fields:
bundle_version: bundle schema version (current:1)created_at: ISO-8601 timestamp of bundle creationpolicy: embedded policy documentpolicy_hash: SHA-256 hash of the normalized policysigning(optional):algorithm:ed25519key_id: optional key identifiersignature: base64 signature ofpolicy_hash
Example:
bundle_version: 1
created_at: "2026-02-23T00:00:00Z"
policy_hash: "abc123..."
policy:
version: 1
defaults:
action: DENY
allow:
- id: openai_api
host: api.openai.com
methods: [POST]
paths: [/v1/*]
secrets: [OPENAI_KEY]
signing:
algorithm: ed25519
key_id: "policy-root-1"
signature: "Base64SignatureHere"
Bundle files follow the same extension rules as policies: .yaml / .yml for YAML, .json for JSON.
Creating a Bundle¶
Sign with an Ed25519 private key (PKCS8 PEM or base64):
oag bundle --policy policy.yaml --out policy.bundle.json --sign-key ./keys/policy-private.pem --key-id policy-root-1
Verifying a Bundle¶
Use oag verify with the Ed25519 public key (X.509 PEM or base64):
oag run, oag doctor, oag explain, and oag test accept the same bundle file path via --policy.
Bundle Enforcement¶
- If
--policy-require-signatureis set, OAG requires a signature and a public key. - If a public key is provided and the policy file is not a bundle, OAG fails fast.
- Bundle signatures are verified against
policy_hash, and OAG verifies thatpolicy_hashmatches the normalized policy.
Key Format Notes¶
- Private keys: Ed25519 PKCS8 PEM or raw base64-encoded PKCS8 bytes.
- Public keys: Ed25519 X.509 PEM or raw base64-encoded X.509 bytes.
If you use PEM files, include the standard headers:
-----BEGIN PRIVATE KEY-----/-----END PRIVATE KEY----------BEGIN PUBLIC KEY-----/-----END PUBLIC KEY-----
Policy Hot-Reload¶
OAG can automatically reload the policy file when it changes on disk. This enables updating rules without restarting the proxy, which is useful for long-running sessions and deployment pipelines that push policy updates.
Enabling Hot-Reload¶
Pass the --watch flag to oag run:
With --watch active, OAG monitors the policy file using the operating system's file notification service (WatchService). When the file is modified, OAG:
- Re-reads the policy file from disk
- Validates the new policy (schema, rules, patterns)
- Normalizes and hashes the new policy
- Atomically swaps the active policy snapshot
If validation fails, the current policy remains active and no traffic is disrupted.
Debouncing¶
File modifications are debounced with a 500ms window. Rapid successive writes (e.g., editor save-then-format) are collapsed into a single reload. This prevents unnecessary reloads from editors that write files in multiple steps.
Reload Behavior¶
- Atomic swap: The policy snapshot is replaced using
@Volatileand@Synchronizedaccess. In-flight requests that already captured a policy reference continue using the old policy. New requests use the updated policy. - Validation-first: The new policy is fully validated before replacing the old one. Invalid policies are rejected with an error audit event; the proxy continues operating with the previous valid policy.
- History tracking: Each successful policy change is recorded in
PolicyService.policyHistory()with a hash and timestamp. - No-op on unchanged: If the file is modified but the normalized policy hash is identical, no swap occurs and
changedisfalsein the audit event.
Reload Audit Events¶
Every reload attempt (success or failure) emits a policy_reload audit event:
Successful reload (policy changed):
{
"event_type": "policy_reload",
"schema_version": "3",
"oag_version": "0.1.0",
"previous_policy_hash": "abc123...",
"new_policy_hash": "def456...",
"changed": true,
"success": true,
"agent_id": "agent-1",
"session_id": "session-1",
"timestamp": "2026-02-23T12:00:00Z"
}
Successful reload (no change):
{
"event_type": "policy_reload",
"schema_version": "3",
"previous_policy_hash": "abc123...",
"new_policy_hash": "abc123...",
"changed": false,
"success": true
}
Failed reload (invalid policy):
{
"event_type": "policy_reload",
"schema_version": "3",
"previous_policy_hash": "abc123...",
"new_policy_hash": null,
"changed": false,
"success": false,
"error_message": "Policy validation failed:\n- allow[0].host: Missing or empty"
}
File Watcher Details¶
- Scope: Only the policy file itself is monitored. Changes to other files in the same directory are ignored.
- Platform: Uses Java NIO
WatchService, which maps toinotifyon Linux,FSEventson macOS, andReadDirectoryChangesWon Windows. - Thread: The watcher runs as a coroutine on
Dispatchers.IO. It does not block the proxy's request-handling threads. - Shutdown: The watcher is closed when the proxy shuts down. The coroutine is cancelled via
watchJob?.cancel().
Hot-Reload Verbose Logging¶
When --verbose is also active, reload events are logged to stderr:
On failure:
Hot-Reload Limitations¶
- Bundle files: When using signed policy bundles (
.bundle.json), the watcher monitors the bundle file. The signature is re-verified on each reload. - Config-dir mode: When using
--config-dir, the watcher monitors<config-dir>/policy.yaml. - No remote sources: The watcher only monitors local files. For remote policy sources, use an external tool to download and write the file locally.
Policy Linting and Conflict Detection¶
OAG includes a built-in policy linter that detects common misconfigurations, shadowed rules, and overlapping scopes. Use it in CI to catch policy issues before deployment.
Lint CLI Usage¶
JSON output for CI integration:
Exit codes:
- 0 — no warnings
- 1 — warnings found (or invalid policy)
Lint Rules¶
UNSAFE_DEFAULT_ALLOW — defaults.action is set to allow, which disables default-deny security posture. Requests not matched by any deny rule will be forwarded.
SHADOWED_RULE — A later rule in the same section (allow or deny) is unreachable because an earlier rule matches a superset of its scope.
allow:
- id: broad-rule
host: "*.example.com"
- id: narrow-rule # SHADOWED: *.example.com already covers api.example.com
host: api.example.com
The linter checks host, method, and path coverage. Rules with ip_ranges, conditions, body_match, header_match, query_match, or payload_match constraints are considered narrower and cannot shadow other rules.
OVERLAPPING_RULES — An allow rule and a deny rule could match the same request. OAG evaluates deny rules first, so the deny rule takes precedence — but this overlap may indicate unintended policy behavior.
allow:
- id: allow-api
host: api.example.com
methods: [GET, POST]
deny:
- id: deny-post
host: api.example.com
methods: [POST] # OVERLAP: POST matches both rules (deny wins)
UNUSED_SECRET_REF — A rule references a secret ID that is not defined in secret_scopes. This usually means the secret will never be materialized.
allow:
- id: openai-rule
host: api.openai.com
secrets: [openai-key] # WARNING: no secret_scopes entry for 'openai-key'
secret_scopes: []
UNREACHABLE_ALLOW — An allow rule is unreachable because a deny rule matches all of its traffic. Since deny rules are evaluated first, the allow rule will never be reached.
UNSAFE_REGEX — A regex pattern in a rule (body_match, header_match, query_match, payload_match, content_inspection, anchored_patterns, response_rewrites, or data_classification) uses patterns that may be vulnerable to ReDoS (nested quantifiers, excessive length).
Lint JSON Output Schema¶
With warnings:
{
"ok": false,
"warning_count": 2,
"warnings": [
{
"code": "SHADOWED_RULE",
"message": "allow rule 'narrow-rule' is shadowed by earlier rule 'broad-rule'",
"rule_id": "narrow-rule",
"rule_index": 1,
"section": "allow"
},
{
"code": "UNUSED_SECRET_REF",
"message": "allow rule 'openai-rule' references secret 'openai-key' not defined in secret_scopes",
"rule_id": "openai-rule",
"rule_index": 0,
"section": "allow"
}
]
}
Lint CI Integration¶
Add a lint step to your CI pipeline:
The command exits with code 1 when warnings are found, failing the CI step.
Lint works with policy bundles and signature verification:
oag lint --policy policy.bundle.json --policy-public-key ./keys/policy-public.pem --policy-require-signature
Agent Profiles¶
Optional per-agent access control. Profiles restrict which rules an agent can use:
agent_profiles:
- id: code-agent
allowed_rules: [github_api, npm_registry]
max_requests_per_minute: 60
max_body_bytes: 1048576
tags: [ci, automated]
- id: research-agent
denied_rules: [internal_api]
max_requests_per_minute: 120
Fields:
- id: Profile identifier (required). Matched against --agent at runtime.
- allowed_rules: Whitelist of rule IDs this agent can use. When set, only listed rules apply.
- denied_rules: Blacklist of rule IDs blocked for this agent.
- max_requests_per_minute: Optional rate limit per agent.
- max_body_bytes: Optional body size limit for this agent.
- tags: Optional labels for categorization.
When an agent is blocked by its profile, the reason code is agent_profile_denied.
References¶
- HTTP semantics and redirect behavior: RFC 9110. [RFC9110]
- CONNECT method behavior: RFC 9110. [RFC9110]
- SSRF and proxy bypass patterns: OWASP SSRF Prevention Cheat Sheet. [OWASP-SSRF]
See concepts.md for full reference list.
Examples¶
See examples/policy-examples.md for ready-to-use policy recipes.